From 6854cb3f4d8219cf1829e32122eb2502a916eae9 Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sat, 1 Feb 2020 09:05:48 +0100 Subject: initial checkin --- includes/class.notify.php | 1114 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1114 insertions(+) create mode 100644 includes/class.notify.php (limited to 'includes/class.notify.php') diff --git a/includes/class.notify.php b/includes/class.notify.php new file mode 100644 index 0000000..386158a --- /dev/null +++ b/includes/class.notify.php @@ -0,0 +1,1114 @@ +address($task_id, $type); + } + + if (!is_array($to)) { + settype($to, 'array'); + } + + if (!count($to)) { + return false; + } + + $languages = array(); + $emails = array(); + $jabbers = array(); + $onlines = array(); + + if (isset($to[0])) { + foreach ($to[0] as $recipient) { + if (!empty($recipient['lang'])) { + $lang = $recipient['lang']; + } else if (!empty($proj_lang)) { + $lang = $proj_lang; + } else { + $lang = $fs->prefs['lang_code']; + } + $emails[$lang][] = $recipient['recipient']; + if (!in_array($lang, $languages)) { + $languages[] = $lang; + } + } + } + + if (isset($to[1])) { + foreach ($to[1] as $recipient) { + if (!empty($recipient['lang'])) { + $lang = $recipient['lang']; + } else if (!empty($proj_lang)) { + $lang = $proj_lang; + } else { + $lang = $fs->prefs['lang_code']; + } + $jabbers[$lang][] = $recipient['recipient']; + if (!in_array($lang, $languages)) { + $languages[] = $lang; + } + } + } + /* + if (isset($to[2])) { + foreach ($to[2] as $recipient) { + $lang = $recipient['lang']; + if ($lang == 'j') + echo "
Error 3!
"; + $onlines[$lang][] = $recipient['recipient']; + if (!in_array($lang, $languages)) { + $languages[] = $lang; + } + } + } + */ + + $result = true; + foreach ($languages as $lang) { + $msg = $this->generateMsg($type, $task_id, $info, $lang); + if (isset($emails[$lang]) && ($ntype == NOTIFY_EMAIL || $ntype == NOTIFY_BOTH)) { + if (!$this->sendEmail($emails[$lang], $msg[0], $msg[1], $task_id)) { + $result = false; + } + } + + if (isset($jabbers[$lang]) && ($ntype == NOTIFY_JABBER || $ntype == NOTIFY_BOTH)) { + if (!$this->storeJabber($jabbers[$lang], $msg[0], $msg[1])) { + $result = false; + } + } + + // Get rid of undefined offset 2 when notify type is explicitly set, + // in these cases caller really has not set offset 2. Track down the + // callers later. + /* + if (isset($onlines[$lang]) && ($ntype != NOTIFY_EMAIL && $ntype != NOTIFY_JABBER)) { + if (!$this->StoreOnline($onlines[$lang], $msg[2], $msg[3], $task_id)) { + $result = false; + } + } + */ + } + return $result; + + // End of Create() function + } + + function storeOnline($to, $subject, $body, $online, $task_id = null) { + global $db, $fs; + + if (!count($to)) { + return false; + } + + $date = time(); + + // store notification in table + $db->query("INSERT INTO {notification_messages} + (message_subject, message_body, time_created) + VALUES (?, ?, ?)", array($online, '', $date) + ); + + // grab notification id + /* + $result = $db->query("SELECT message_id FROM {notification_messages} + WHERE time_created = ? ORDER BY message_id DESC", array($date), 1); + + $row = $db->fetchRow($result); + $message_id = $row['message_id']; + */ + $message_id = $db->insert_ID(); + // If message could not be inserted for whatever reason... + if (!$message_id) { + return false; + } + + // make sure every user is only added once + settype($to, 'array'); + $to = array_unique($to); + + foreach ($to as $jid) { + // store each recipient in table + $db->query("INSERT INTO {notification_recipients} + (notify_method, message_id, notify_address) + VALUES (?, ?, ?)", array('o', $message_id, $jid) + ); + } + + return true; + } + + static function getUnreadNotifications() { + global $db, $fs, $user; + + $notifications = $db->query('SELECT r.recipient_id, m.message_subject + FROM {notification_recipients} r + JOIN {notification_messages} m ON r.message_id = m.message_id + WHERE r.notify_method = ? AND notify_address = ?', + array('o', $user['user_id'])); + return $db->fetchAllArray($notifications); + } + + static function NotificationsHaveBeenRead($ids) { + global $db, $fs, $user; + + $readones = join(",", array_map('intval', $ids)); + if($readones !=''){ + $db->query(" + DELETE FROM {notification_recipients} + WHERE message_id IN ($readones) + AND notify_method = ? + AND notify_address = ?", + array('o', $user['user_id'] + ) + ); + } + } + + // {{{ Store Jabber messages for sending later + function storeJabber( $to, $subject, $body ) + { + global $db, $fs; + + if (empty($fs->prefs['jabber_server']) + || empty($fs->prefs['jabber_port']) + || empty($fs->prefs['jabber_username']) + || empty($fs->prefs['jabber_password'])) { + return false; + } + + if (empty($to)) { + return false; + } + + $date = time(); + + // store notification in table + $db->query("INSERT INTO {notification_messages} + (message_subject, message_body, time_created) + VALUES (?, ?, ?)", + array($subject, $body, $date) + ); + + // grab notification id + /* + $result = $db->query("SELECT message_id FROM {notification_messages} + WHERE time_created = ? ORDER BY message_id DESC", + array($date), 1); + + $row = $db->fetchRow($result); + $message_id = $row['message_id']; + */ + $message_id = $db->insert_ID(); + // If message could not be inserted for whatever reason... + if (!$message_id) { + return false; + } + + settype($to, 'array'); + + $duplicates = array(); + foreach ($to as $jid) { + // make sure every recipient is only added once + if (in_array($jid, $duplicates)) { + continue; + } + $duplicates[] = $jid; + // store each recipient in table + $db->query("INSERT INTO {notification_recipients} + (notify_method, message_id, notify_address) + VALUES (?, ?, ?)", + array('j', $message_id, $jid) + ); + + } + + return true; + } // }}} + + static function jabberRequestAuth($email) + { + global $fs; + + include_once BASEDIR . '/includes/class.jabber2.php'; + + if (empty($fs->prefs['jabber_server']) + || empty($fs->prefs['jabber_port']) + || empty($fs->prefs['jabber_username']) + || empty($fs->prefs['jabber_password'])) { + return false; + } + + $JABBER = new Jabber($fs->prefs['jabber_username'] . '@' . $fs->prefs['jabber_server'], + $fs->prefs['jabber_password'], + $fs->prefs['jabber_ssl'], + $fs->prefs['jabber_port']); + $JABBER->login(); + $JABBER->send(""); + $JABBER->disconnect(); + } + + // {{{ send Jabber messages that were stored earlier + function sendJabber() + { + global $db, $fs; + + include_once BASEDIR . '/includes/class.jabber2.php'; + + if ( empty($fs->prefs['jabber_server']) + || empty($fs->prefs['jabber_port']) + || empty($fs->prefs['jabber_username']) + || empty($fs->prefs['jabber_password'])) { + return false; + } + + // get listing of all pending jabber notifications + $result = $db->query("SELECT DISTINCT message_id + FROM {notification_recipients} + WHERE notify_method='j'"); + + if (!$db->countRows($result)) { + return false; + } + + $JABBER = new Jabber($fs->prefs['jabber_username'] . '@' . $fs->prefs['jabber_server'], + $fs->prefs['jabber_password'], + $fs->prefs['jabber_ssl'], + $fs->prefs['jabber_port']); + $JABBER->login(); + + // we have notifications to process - connect + $JABBER->log("We have notifications to process..."); + $JABBER->log("Starting Jabber session:"); + + $ids = array(); + + while ( $row = $db->fetchRow($result) ) { + $ids[] = $row['message_id']; + } + + $desired = join(",", array_map('intval', $ids)); + $JABBER->log("message ids to send = {" . $desired . "}"); + + // removed array usage as it's messing up the select + // I suspect this is due to the variable being comma separated + // Jamin W. Collins 20050328 + $notifications = $db->query(" + SELECT * FROM {notification_messages} + WHERE message_id IN ($desired) + ORDER BY time_created ASC" + ); + $JABBER->log("number of notifications {" . $db->countRows($notifications) . "}"); + + // loop through notifications + while ( $notification = $db->fetchRow($notifications) ) { + $subject = $notification['message_subject']; + $body = $notification['message_body']; + + $JABBER->log("Processing notification {" . $notification['message_id'] . "}"); + $recipients = $db->query(" + SELECT * FROM {notification_recipients} + WHERE message_id = ? + AND notify_method = 'j'", + array($notification['message_id']) + ); + + // loop through recipients + while ($recipient = $db->fetchRow($recipients) ) { + $jid = $recipient['notify_address']; + $JABBER->log("- attempting send to {" . $jid . "}"); + + // send notification + if ($JABBER->send_message($jid, $body, $subject, 'normal')) { + // delete entry from notification_recipients + $result = $db->query("DELETE FROM {notification_recipients} + WHERE message_id = ? + AND notify_method = 'j' + AND notify_address = ?", + array($notification['message_id'], $jid) + ); + $JABBER->log("- notification sent"); + } else { + $JABBER->log("- notification not sent"); + } + } + // check to see if there are still recipients for this notification + $result = $db->query("SELECT * FROM {notification_recipients} + WHERE message_id = ?", + array($notification['message_id']) + ); + + if ( $db->countRows($result) == 0 ) { + $JABBER->log("No further recipients for message id {" . $notification['message_id'] . "}"); + // remove notification no more recipients + $result = $db->query("DELETE FROM {notification_messages} + WHERE message_id = ?", + array($notification['message_id']) + ); + $JABBER->log("- Notification deleted"); + } + } + + // disconnect from server + $JABBER->disconnect(); + $JABBER->log("Disconnected from Jabber server"); + + return true; + } // }}} + // {{{ send email + function sendEmail($to, $subject, $body, $task_id = null) + { + global $fs, $proj, $user; + + if (empty($to) || empty($to[0])) { + return; + } + + // Do we want to use a remote mail server? + if (!empty($fs->prefs['smtp_server'])) { + + // connection... SSL, TLS or none + if ($fs->prefs['email_tls']) { + $swiftconn = Swift_SmtpTransport::newInstance($fs->prefs['smtp_server'], 587, 'tls'); + } else if ($fs->prefs['email_ssl']) { + $swiftconn = Swift_SmtpTransport::newInstance($fs->prefs['smtp_server'], 465, 'ssl'); + } else { + $swiftconn = Swift_SmtpTransport::newInstance($fs->prefs['smtp_server']); + } + + if ($fs->prefs['smtp_user']) { + $swiftconn->setUsername($fs->prefs['smtp_user']); + } + + if ($fs->prefs['smtp_pass']){ + $swiftconn->setPassword($fs->prefs['smtp_pass']); + } + + if(defined('FS_SMTP_TIMEOUT')) { + $swiftconn->setTimeout(FS_SMTP_TIMEOUT); + } + // Use php's built-in mail() function + } else { + $swiftconn = Swift_MailTransport::newInstance(); + } + + // Make plaintext URLs into hyperlinks, but don't disturb existing ones! + $htmlbody = preg_replace("/(?$2', $body); + $htmlbody = str_replace("\n","
", $htmlbody); + + // Those constants used were introduced in 5.4. + if (version_compare(phpversion(), '5.4.0', '<')) { + $plainbody= html_entity_decode(strip_tags($body)); + } else { + $plainbody= html_entity_decode(strip_tags($body), ENT_COMPAT | ENT_HTML401, 'utf-8'); + } + + $swift = Swift_Mailer::newInstance($swiftconn); + + if(defined('FS_MAIL_LOGFILE')) { + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $swift->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); + } + + $message = new Swift_Message($subject); + if (isset($fs->prefs['emailNoHTML']) && $fs->prefs['emailNoHTML'] == '1'){ + $message->setBody($plainbody, 'text/plain'); + }else{ + $message->setBody($htmlbody, 'text/html'); + $message->addPart($plainbody, 'text/plain'); + } + + $type = $message->getHeaders()->get('Content-Type'); + $type->setParameter('charset', 'utf-8'); + + $message->getHeaders()->addTextHeader('Precedence', 'list'); + $message->getHeaders()->addTextHeader('X-Mailer', 'Flyspray'); + + if ($proj->prefs['notify_reply']) { + $message->setReplyTo($proj->prefs['notify_reply']); + } + + if (isset($task_id)) { + $hostdata = parse_url($GLOBALS['baseurl']); + $inreplyto = sprintf('', $task_id, $hostdata['host']); + // see http://cr.yp.to/immhf/thread.html this does not seems to work though :( + $message->getHeaders()->addTextHeader('In-Reply-To', $inreplyto); + $message->getHeaders()->addTextHeader('References', $inreplyto); + } + + // accepts string, array, or Swift_Address + if( is_array($to) && count($to)>1 ){ + $message->setTo($fs->prefs['admin_email']); + $message->setBcc($to); + } else{ + $message->setTo($to); + } + $message->setFrom(array($fs->prefs['admin_email'] => $proj->prefs['project_title'])); + $swift->send($message); + + if(defined('FS_MAIL_LOGFILE')) { + if(is_writable(dirname(FS_MAIL_LOGFILE))) { + if($fh = fopen(FS_MAIL_LOGFILE, 'ab')) { + fwrite($fh, $logger->dump()); + fwrite($fh, php_uname()); + fclose($fh); + } + } + } + + return true; + } //}}} + // {{{ create a message for any occasion + function generateMsg($type, $task_id, $arg1 = '0', $lang) { + global $db, $fs, $user, $proj; + + // Get the task details + $task_details = Flyspray::getTaskDetails($task_id); + if ($task_id) { + $proj = new Project($task_details['project_id']); + } + + // Set the due date correctly + if ($task_details['due_date'] == '0') { + $due_date = tL('undecided', $lang); + } else { + $due_date = formatDate($task_details['due_date']); + } + + // Set the due version correctly + if ($task_details['closedby_version'] == '0') { + $task_details['due_in_version_name'] = tL('undecided', $lang); + } + + // Get the string of modification + $notify_type_msg = array( + 0 => tL('none'), + NOTIFY_TASK_OPENED => tL('taskopened', $lang), + NOTIFY_TASK_CHANGED => tL('pm.taskchanged', $lang), + NOTIFY_TASK_CLOSED => tL('taskclosed', $lang), + NOTIFY_TASK_REOPENED => tL('pm.taskreopened', $lang), + NOTIFY_DEP_ADDED => tL('pm.depadded', $lang), + NOTIFY_DEP_REMOVED => tL('pm.depremoved', $lang), + NOTIFY_COMMENT_ADDED => tL('commentadded', $lang), + NOTIFY_ATT_ADDED => tL('attachmentadded', $lang), + NOTIFY_REL_ADDED => tL('relatedadded', $lang), + NOTIFY_OWNERSHIP => tL('ownershiptaken', $lang), + NOTIFY_PM_REQUEST => tL('pmrequest', $lang), + NOTIFY_PM_DENY_REQUEST => tL('pmrequestdenied', $lang), + NOTIFY_NEW_ASSIGNEE => tL('newassignee', $lang), + NOTIFY_REV_DEP => tL('revdepadded', $lang), + NOTIFY_REV_DEP_REMOVED => tL('revdepaddedremoved', $lang), + NOTIFY_ADDED_ASSIGNEES => tL('assigneeadded', $lang), + ); + + // Generate the nofication message + if (isset($proj->prefs['notify_subject']) && !$proj->prefs['notify_subject']) { + $proj->prefs['notify_subject'] = '[%p][#%t] %s'; + } + if (!isset($proj->prefs['notify_subject']) || + $type == NOTIFY_CONFIRMATION || + $type == NOTIFY_ANON_TASK || + $type == NOTIFY_PW_CHANGE || + $type == NOTIFY_NEW_USER || + $type == NOTIFY_OWN_REGISTRATION) { + $subject = tL('notifyfromfs', $lang); + } else { + $subject = strtr($proj->prefs['notify_subject'], array('%p' => $proj->prefs['project_title'], + '%s' => $task_details['item_summary'], + '%t' => $task_id, + '%a' => $notify_type_msg[$type], + '%u' => $user->infos['user_name'])); + } + + $subject = strtr($subject, "\n", ''); + + + /* ------------------------------- + | List of notification types: | + | 1. Task opened | + | 2. Task details changed | + | 3. Task closed | + | 4. Task re-opened | + | 5. Dependency added | + | 6. Dependency removed | + | 7. Comment added | + | 8. Attachment added | + | 9. Related task added | + |10. Taken ownership | + |11. Confirmation code | + |12. PM request | + |13. PM denied request | + |14. New assignee | + |15. Reversed dep | + |16. Reversed dep removed | + |17. Added to assignees list | + |18. Anon-task opened | + |19. Password change | + |20. New user | + |21. User registration | + ------------------------------- + */ + + $body = tL('donotreply', $lang) . "\n\n"; + $online = ''; + + // {{{ New task opened + if ($type == NOTIFY_TASK_OPENED) { + $body .= tL('newtaskopened', $lang) . " \n\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ") \n\n"; + $body .= tL('attachedtoproject', $lang) . ' - ' . $task_details['project_title'] . "\n"; + $body .= tL('summary', $lang) . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('tasktype', $lang) . ' - ' . $task_details['tasktype_name'] . "\n"; + $body .= tL('category', $lang) . ' - ' . $task_details['category_name'] . "\n"; + $body .= tL('status', $lang) . ' - ' . $task_details['status_name'] . "\n"; + $body .= tL('assignedto', $lang) . ' - ' . implode(', ', $task_details['assigned_to_name']) . "\n"; + $body .= tL('operatingsystem', $lang) . ' - ' . $task_details['os_name'] . "\n"; + $body .= tL('severity', $lang) . ' - ' . $task_details['severity_name'] . "\n"; + $body .= tL('priority', $lang) . ' - ' . $task_details['priority_name'] . "\n"; + $body .= tL('reportedversion', $lang) . ' - ' . $task_details['reported_version_name'] . "\n"; + $body .= tL('dueinversion', $lang) . ' - ' . $task_details['due_in_version_name'] . "\n"; + $body .= tL('duedate', $lang) . ' - ' . $due_date . "\n"; + $body .= tL('details', $lang) . ' - ' . $task_details['detailed_desc'] . "\n\n"; + + if ($arg1 == 'files') { + $body .= tL('fileaddedtoo', $lang) . "\n\n"; + $subject .= ' (' . tL('attachmentadded', $lang) . ')'; + } + + $body .= tL('moreinfo', $lang) . "\n"; + $body .= createURL('details', $task_id); + + $online .= tL('newtaskopened', $lang) . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + $online .= tL('attachedtoproject', $lang) . ' - ' . $task_details['project_title'] . ". "; + $online .= tL('summary', $lang) . ' - ' . $task_details['item_summary']; + } // }}} + // {{{ Task details changed + if ($type == NOTIFY_TASK_CHANGED) { + $translation = array('priority_name' => tL('priority', $lang), + 'severity_name' => tL('severity', $lang), + 'status_name' => tL('status', $lang), + 'assigned_to_name' => tL('assignedto', $lang), + 'due_in_version_name' => tL('dueinversion', $lang), + 'reported_version_name' => tL('reportedversion', $lang), + 'tasktype_name' => tL('tasktype', $lang), + 'os_name' => tL('operatingsystem', $lang), + 'category_name' => tL('category', $lang), + 'due_date' => tL('duedate', $lang), + 'percent_complete' => tL('percentcomplete', $lang), + 'mark_private' => tL('visibility', $lang), + 'item_summary' => tL('summary', $lang), + 'detailed_desc' => tL('taskedited', $lang), + 'project_title' => tL('attachedtoproject', $lang), + 'estimated_effort' => tL('estimatedeffort', $lang)); + + $body .= tL('taskchanged', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ': ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n"; + + $online .= tL('taskchanged', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary']; + + foreach ($arg1 as $change) { + if ($change[0] == 'assigned_to_name') { + $change[1] = implode(', ', $change[1]); + $change[2] = implode(', ', $change[2]); + } + + if ($change[0] == 'detailed_desc') { + $body .= $translation[$change[0]] . ":\n-------\n" . $change[2] . "\n-------\n"; + } else { + $body .= $translation[$change[0]] . ': ' . ( ($change[1]) ? $change[1] : '[-]' ) . ' -> ' . ( ($change[2]) ? $change[2] : '[-]' ) . "\n"; + } + } + $body .= "\n" . tL('moreinfo', $lang) . "\n"; + $body .= createURL('details', $task_id); + } // }}} + // {{{ Task closed + if ($type == NOTIFY_TASK_CLOSED) { + $body .= tL('notify.taskclosed', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n\n"; + $body .= tL('reasonforclosing', $lang) . ' ' . $task_details['resolution_name'] . "\n"; + + if (!empty($task_details['closure_comment'])) { + $body .= tL('closurecomment', $lang) . ' ' . $task_details['closure_comment'] . "\n\n"; + } + + $body .= tL('moreinfo', $lang) . "\n"; + $body .= createURL('details', $task_id); + + $online .= tL('notify.taskclosed', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ Task re-opened + if ($type == NOTIFY_TASK_REOPENED) { + $body .= tL('notify.taskreopened', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n\n"; + $body .= tL('moreinfo', $lang) . "\n"; + $body .= createURL('details', $task_id); + + $online .= tL('notify.taskreopened', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ Dependency added + if ($type == NOTIFY_DEP_ADDED) { + $depend_task = Flyspray::getTaskDetails($arg1); + + $body .= tL('newdep', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n"; + $body .= createURL('details', $task_id) . "\n\n\n"; + $body .= tL('newdepis', $lang) . ':' . "\n\n"; + $body .= 'FS#' . $depend_task['task_id'] . ' - ' . $depend_task['item_summary'] . "\n"; + $body .= createURL('details', $depend_task['task_id']); + + $online .= tL('newdep', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ Dependency removed + if ($type == NOTIFY_DEP_REMOVED) { + $depend_task = Flyspray::getTaskDetails($arg1); + + $body .= tL('notify.depremoved', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n"; + $body .= createURL('details', $task_id) . "\n\n\n"; + $body .= tL('removeddepis', $lang) . ':' . "\n\n"; + $body .= 'FS#' . $depend_task['task_id'] . ' - ' . $depend_task['item_summary'] . "\n"; + $body .= createURL('details', $depend_task['task_id']); + + $online .= tL('notify.depremoved', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ Comment added + if ($type == NOTIFY_COMMENT_ADDED) { + // Get the comment information + $result = $db->query("SELECT comment_id, comment_text + FROM {comments} + WHERE user_id = ? + AND task_id = ? + ORDER BY comment_id DESC", array($user->id, $task_id), '1'); + $comment = $db->fetchRow($result); + + $body .= tL('notify.commentadded', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n\n"; + $body .= "----------\n"; + $body .= $comment['comment_text'] . "\n"; + $body .= "----------\n\n"; + + if ($arg1 == 'files') { + $body .= tL('fileaddedtoo', $lang) . "\n\n"; + $subject .= ' (' . tL('attachmentadded', $lang) . ')'; + } + + $body .= tL('moreinfo', $lang) . "\n"; + $body .= createURL('details', $task_id) . '#comment' . $comment['comment_id']; + + $online .= tL('notify.commentadded', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ Attachment added + if ($type == NOTIFY_ATT_ADDED) { + $body .= tL('newattachment', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n\n"; + $body .= tL('moreinfo', $lang) . "\n"; + $body .= createURL('details', $task_id); + + $online .= tL('newattachment', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ Related task added + if ($type == NOTIFY_REL_ADDED) { + $related_task = Flyspray::getTaskDetails($arg1); + + $body .= tL('notify.relatedadded', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n"; + $body .= createURL('details', $task_id) . "\n\n\n"; + $body .= tL('relatedis', $lang) . ':' . "\n\n"; + $body .= 'FS#' . $related_task['task_id'] . ' - ' . $related_task['item_summary'] . "\n"; + $body .= createURL('details', $related_task['task_id']); + + $online .= tL('notify.relatedadded', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ Ownership taken + if ($type == NOTIFY_OWNERSHIP) { + $body .= implode(', ', $task_details['assigned_to_name']) . ' ' . tL('takenownership', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n\n"; + $body .= tL('moreinfo', $lang) . "\n"; + $body .= createURL('details', $task_id); + + $online .= implode(', ', $task_details['assigned_to_name']) . ' ' . tL('takenownership', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "."; + } // }}} + // {{{ Confirmation code + if ($type == NOTIFY_CONFIRMATION) { + $body .= tL('noticefrom', $lang) . " {$proj->prefs['project_title']}\n\n" + . tL('addressused', $lang) . "\n\n" + . " {$arg1[0]}index.php?do=register&magic_url={$arg1[1]} \n\n" + // In case that spaces in the username have been removed + . tL('username', $lang) . ': ' . $arg1[2] . "\n" + . tL('confirmcodeis', $lang) . " $arg1[3] \n\n"; + + $online = $body; + } // }}} + // {{{ Pending PM request + if ($type == NOTIFY_PM_REQUEST) { + $body .= tL('requiresaction', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho') . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n\n"; + $body .= tL('moreinfo', $lang) . "\n"; + $body .= createURL('details', $task_id); + + $online .= tL('requiresaction', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ PM request denied + if ($type == NOTIFY_PM_DENY_REQUEST) { + $body .= tL('pmdeny', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n\n"; + $body .= tL('denialreason', $lang) . ':' . "\n"; + $body .= $arg1 . "\n\n"; + $body .= tL('moreinfo', $lang) . "\n"; + $body .= createURL('details', $task_id); + + $online .= tL('pmdeny', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ New assignee + if ($type == NOTIFY_NEW_ASSIGNEE) { + $body .= tL('assignedtoyou', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n\n"; + $body .= tL('moreinfo', $lang) . "\n"; + $body .= createURL('details', $task_id); + + $online .= tL('assignedtoyou', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ Reversed dep + if ($type == NOTIFY_REV_DEP) { + $depend_task = Flyspray::getTaskDetails($arg1); + + $body .= tL('taskwatching', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n"; + $body .= createURL('details', $task_id) . "\n\n\n"; + $body .= tL('isdepfor', $lang) . ':' . "\n\n"; + $body .= 'FS#' . $depend_task['task_id'] . ' - ' . $depend_task['item_summary'] . "\n"; + $body .= createURL('details', $depend_task['task_id']); + + $online .= tL('taskwatching', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ Reversed dep - removed + if ($type == NOTIFY_REV_DEP_REMOVED) { + $depend_task = Flyspray::getTaskDetails($arg1); + + $body .= tL('taskwatching', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n"; + $body .= createURL('details', $task_id) . "\n\n\n"; + $body .= tL('isnodepfor', $lang) . ':' . "\n\n"; + $body .= 'FS#' . $depend_task['task_id'] . ' - ' . $depend_task['item_summary'] . "\n"; + $body .= createURL('details', $depend_task['task_id']); + + $online .= tL('taskwatching', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ User added to assignees list + if ($type == NOTIFY_ADDED_ASSIGNEES) { + $body .= tL('useraddedtoassignees', $lang) . "\n\n"; + $body .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . "\n"; + $body .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . ")\n"; + $body .= createURL('details', $task_id); + + $online .= tL('useraddedtoassignees', $lang) . ". "; + $online .= 'FS#' . $task_id . ' - ' . $task_details['item_summary'] . ". "; + $online .= tL('userwho', $lang) . ' - ' . $user->infos['real_name'] . ' (' . $user->infos['user_name'] . "). "; + } // }}} + // {{{ Anon-task has been opened + if ($type == NOTIFY_ANON_TASK) { + $body .= tL('thankyouforbug', $lang) . "\n\n"; + $body .= createURL('details', $task_id, null, array('task_token' => $arg1)) . "\n\n"; + + $online .= tL('thankyouforbug') . ""; + } // }}} + // {{{ Password change + if ($type == NOTIFY_PW_CHANGE) { + $body = tL('magicurlmessage', $lang) . " \n" + . "{$arg1[0]}index.php?do=lostpw&magic_url=$arg1[1]\n\n" + . tL('messagefrom', $lang) . $arg1[0]; + $online = $body; + } // } }} + // {{{ New user + if ($type == NOTIFY_NEW_USER) { + $body = tL('newuserregistered', $lang) . " \n\n" + . tL('username', $lang) . ': ' . $arg1[1] . "\n" . + tL('realname', $lang) . ': ' . $arg1[2] . "\n"; + $online = $body; + + if ($arg1[6]) { + $body .= tL('password', $lang) . ': ' . $arg1[5] . "\n"; + } + + $body .= tL('emailaddress', $lang) . ': ' . $arg1[3] . "\n"; + $body .= tL('jabberid', $lang) . ':' . $arg1[4] . "\n\n"; + $body .= tL('messagefrom', $lang) . $arg1[0]; + } // }}} + // {{{ New user him/herself + if ($type == NOTIFY_OWN_REGISTRATION) { + $body = tL('youhaveregistered', $lang) . " \n\n" + . tL('username', $lang) . ': ' . $arg1[1] . "\n" . + tL('realname', $lang) . ': ' . $arg1[2] . "\n"; + $online = $body; + + if ($arg1[6]) { + $body .= tL('password', $lang) . ': ' . $arg1[5] . "\n"; + } + + $body .= tL('emailaddress', $lang) . ': ' . $arg1[3] . "\n"; + $body .= tL('jabberid', $lang) . ':' . $arg1[4] . "\n\n"; + + // Add something here to tell the user whether the registration must + // first be accepted by Administrators or not. And if it had and was + // rejected, the reason. Check first what happening when requests are + // either denied or accepted. + + $body .= tL('messagefrom', $lang) . $arg1[0]; + } // }}} + + $body .= "\n\n" . tL('disclaimer', $lang); + return array(Notifications::fixMsgData($subject), Notifications::fixMsgData($body), $online); + } + +// }}} + + public static function assignRecipients($recipients, &$emails, &$jabbers, &$onlines, $ignoretype = false) { + global $db, $fs, $user; + + if (!is_array($recipients)) { + return false; + } + + foreach ($recipients as $recipient) { + if ($recipient['user_id'] == $user->id && !$user->infos['notify_own']) { + continue; + } + + if (($fs->prefs['user_notify'] == '1' && ($recipient['notify_type'] == NOTIFY_EMAIL || $recipient['notify_type'] == NOTIFY_BOTH) ) || $fs->prefs['user_notify'] == '2' || $ignoretype) { + if (isset($recipient['email_address']) && !empty($recipient['email_address'])) { + $emails[$recipient['email_address']] = array('recipient' => $recipient['email_address'], 'lang' => $recipient['lang_code']); + } + } + + if (($fs->prefs['user_notify'] == '1' && ($recipient['notify_type'] == NOTIFY_JABBER || $recipient['notify_type'] == NOTIFY_BOTH) ) || $fs->prefs['user_notify'] == '3' || $ignoretype) { + if (isset($recipient['jabber_id']) && !empty($recipient['jabber_id']) && $recipient['jabber_id']) { + $jabbers[$recipient['jabber_id']] = array('recipient' => $recipient['jabber_id'], 'lang' => $recipient['lang_code']); + } + } + /* + if ($fs->prefs['user_notify'] == '1' && $recipient['notify_online']) { + $onlines[$recipient['user_id']] = array('recipient' => $recipient['user_id'], 'lang' => $recipient['lang_code']); + } + */ + } + } + + // {{{ Create an address list for specific users + function specificAddresses($users, $ignoretype = false) { + global $db, $fs, $user; + + $emails = array(); + $jabbers = array(); + $onlines = array(); + + if (!is_array($users)) { + settype($users, 'array'); + } + + if (count($users) < 1) { + return array(); + } + + $sql = $db->query('SELECT u.user_id, u.email_address, u.jabber_id, + u.notify_online, u.notify_type, u.notify_own, u.lang_code + FROM {users} u + WHERE' . substr(str_repeat(' user_id = ? OR ', count($users)), 0, -3), array_values($users)); + + self::assignRecipients($db->fetchAllArray($sql), $emails, $jabbers, $onlines, $ignoretype); + + return array($emails, $jabbers, $onlines); + } + +// }}} + + // {{{ Create a standard address list of users (assignees, notif tab and proj addresses) + function address($task_id, $type) { + global $db, $fs, $proj, $user; + + $users = array(); + $emails = array(); + $jabbers = array(); + $onlines = array(); + + $task_details = Flyspray::getTaskDetails($task_id); + + // Get list of users from the notification tab + $get_users = $db->query(' + SELECT * FROM {notifications} n + LEFT JOIN {users} u ON n.user_id = u.user_id + WHERE n.task_id = ?', + array($task_id) + ); + self::assignRecipients($db->fetchAllArray($get_users), $emails, $jabbers, $onlines); + + // Get list of assignees + $get_users = $db->query(' + SELECT * FROM {assigned} a + LEFT JOIN {users} u ON a.user_id = u.user_id + WHERE a.task_id = ?', + array($task_id) + ); + self::assignRecipients($db->fetchAllArray($get_users), $emails, $jabbers, $onlines); + + // Now, we add the project contact addresses... + // ...but only if the task is public + if ($task_details['mark_private'] != '1' + && in_array($type, Flyspray::int_explode(' ', $proj->prefs['notify_types']))) { + + // FIXME! Have to find users preferred language here too, + // must fetch from database. But the address could also be a mailing + // list address and user not exist in database, use fs->prefs in that case, + + $proj_emails = preg_split('/[\s,;]+/', $proj->prefs['notify_email'], -1, PREG_SPLIT_NO_EMPTY); + $desired = implode("','", $proj_emails); + if($desired !=''){ + $get_users = $db->query(" + SELECT DISTINCT u.user_id, u.email_address, u.jabber_id, + u.notify_online, u.notify_type, u.notify_own, u.lang_code + FROM {users} u + WHERE u.email_address IN ('$desired')" + ); + + self::assignRecipients($db->fetchAllArray($get_users), $emails, $jabbers, $onlines); + } + + $proj_jids = explode(',', $proj->prefs['notify_jabber']); + $desired = implode("','", $proj_jids); + if($desired!='') { + $get_users = $db->query(" + SELECT DISTINCT u.user_id, u.email_address, u.jabber_id, + u.notify_online, u.notify_type, u.notify_own, u.lang_code + FROM {users} u + WHERE u.jabber_id IN ('$desired')" + ); + self::assignRecipients($db->fetchAllArray($get_users), $emails, $jabbers, $onlines); + } + + // Now, handle notification addresses that are not assigned to any user... + foreach ($proj_emails as $email) { + if (!array_key_exists($email, $emails)) { + $emails[$email] = array('recipient' => $email, 'lang' => $fs->prefs['lang_code']); + } + } + + foreach ($proj_jids as $jabber) { + if (!array_key_exists($jabber, $jabbers)) { + $jabbers[$jabber] = array('recipient' => $jabber, 'lang' => $fs->prefs['lang_code']); + } + } + /* + echo "
";
+            echo var_dump($proj_emails);
+            echo var_dump($proj_jids);
+            echo "
"; + */ + // End of checking if a task is private + } + // Send back three arrays containing the notification addresses + return array($emails, $jabbers, $onlines); + } + +// }}} + + // {{{ Fix the message data + /** + * fixMsgData + * a 0.9.9.x ONLY workaround for the "truncated email problem" + * based on code Henri Sivonen (http://hsivonen.iki.fi) + * @param mixed $data + * @access public + * @return void + */ + function fixMsgData($data) + { + // at the first step, remove all NUL bytes + //users with broken databases encoding can give us this :( + $data = str_replace(chr(0), '', $data); + + //then remove all invalid utf8 secuences + $UTF8_BAD = + '([\x00-\x7F]'. # ASCII (including control chars) + '|[\xC2-\xDF][\x80-\xBF]'. # non-overlong 2-byte + '|\xE0[\xA0-\xBF][\x80-\xBF]'. # excluding overlongs + '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # straight 3-byte + '|\xED[\x80-\x9F][\x80-\xBF]'. # excluding surrogates + '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # planes 1-3 + '|[\xF1-\xF3][\x80-\xBF]{3}'. # planes 4-15 + '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # plane 16 + '|(.{1}))'; # invalid byte + + $valid_data = ''; + + while (preg_match('/'.$UTF8_BAD.'/S', $data, $matches)) { + if ( !isset($matches[2])) { + $valid_data .= $matches[0]; + } else { + $valid_data .= '?'; + } + $data = substr($data, strlen($matches[0])); + } + return $valid_data; + } //}}} + +// End of Notify class +} -- cgit v1.2.3-70-g09d2