["requiredvalue"=>true,"default"=>$admin_resource_access_notifications],"actions_resource_requests" =>["requiredvalue"=>false,"default"=>true]] * or * ["user_pref_system_management_notifications" => true] * * All preferences must be set to the required values for the notification to be sent * */ public $user_preference; /** * @var array $eventdata Optional array for linking the system message to a specific activity e.g. resource or account request so that it can be deleted once the request has been processed. * ["type"] e.g. MANAGED_REQUEST * ["ref"] */ public $eventdata = []; /** * Set the notification message * * @param string $text Text or $lang string using the 'lang_' prefix * @param array $find Array of find strings to use for str_replace() in $lang strings * @param array $replace Array of replace strings to use for str_replace() * * @return void */ public function set_text($text, $find = [], $replace = []) { $this->message_parts = [[$text, $find, $replace]]; } /** * Append text to the notification message * * @param string $text Text or $lang string using the 'lang_' prefix * @param array $find Array of find strings to use for str_replace() in $lang strings * @param array $replace Array of replace strings to use for str_replace() * @return void */ public function append_text($text, $find = [], $replace = []) { $this->message_parts[] = [$text, $find, $replace]; } /** * Append multiple text elements to the notification message * * @param array $messages Array of text components as per append_text() * @return void */ public function append_text_multi($textarr) { $this->message_parts = array_merge($this->message_parts, $textarr); } /** * Prepend text component to the notification message * * @param string $text Text or $lang string using the 'lang_' prefix * @param array $find Array of find strings to use for str_replace() in $lang strings * @param array $replace Array of replace strings to use for str_replace() * @return void */ public function prepend_text($text, $find = [], $replace = []) { array_unshift($this->message_parts, [$text, $find, $replace]); } /** * Prepend multiple text elements to the notification message * * @param array $messages Array of text components as per append_text() * @return void */ public function prepend_text_multi($textarr) { // Loop in reverse order so that the parts get ordered correctly at start for ($n = count($textarr); $n--; $n >= 0) { array_unshift($this->message_parts, $textarr[$n]); } } /** * Set the notification subject * * @param string $text Text or $lang string using the 'lang_' prefix * @param array $find Array of find strings to use for str_replace() in $lang strings * @param array $replace Array of replace strings to use for str_replace() * * @return void */ public function set_subject($text, $find = [], $replace = []) { $this->subject = [[$text, $find, $replace]]; } /** * Append text to the notification subject * * @param string $text Text or $lang string using the 'lang_' prefix * @param array $find Array of find strings to use for str_replace() in $lang strings * @param array $replace Array of replace strings to use for str_replace() * @return void */ public function append_subject($text, $find = [], $replace = []) { $this->subject[] = [$text, $find, $replace]; } /** * Get the message text, by default this is resolved into single string with text translated and with the find/replace completed * Note that if not returning raw data the correct $lang must be set by before this is called * @param bool $unresolved Return the raw message parts to use in another message object. False by default * * @return string|array */ public function get_text($unresolved = false) { global $lang; if ($unresolved) { return $this->message_parts; } $messagetext = ""; foreach ($this->message_parts as $message_part) { $text = $message_part[0]; if (substr($text, 0, 5) == "lang_") { $langkey = substr($text, 5); $text = str_replace('[applicationname]', $GLOBALS['applicationname'], $lang[$langkey]); } if (substr($text, 0, 5) == "i18n_") { $i18n_string = substr($text, 5); $text = i18n_get_translated($i18n_string); } if (isset($message_part[1]) && isset($message_part[2]) && count($message_part[1]) == count($message_part[2])) { $text = str_replace($message_part[1], $message_part[2], $text); } $messagetext .= $text; } return $messagetext; } public function get_subject() { global $lang; $fullsubject = ""; foreach ($this->subject as $subjectpart) { $text = $subjectpart[0]; if (substr($text, 0, 5) == "lang_") { $langkey = substr($text, 5); $text = $lang[$langkey]; } if (isset($subjectpart[1]) && isset($subjectpart[2])) { $text = str_replace($subjectpart[1], $subjectpart[2], $text); } $fullsubject .= $text; } return $fullsubject; } } /** * Gets messages for a given user (returns true if there are messages, false if not) * Note that messages are passed by reference. * * @param array $messages Array that will be populated by messages. Passed by reference * @param int $user User ID * @param bool $get_all Retrieve all messages? Setting to TRUE will include all seen and expired messages * @param bool $sort Sort by message ID in ascending or descending order * @param string $order_by Order of messages returned * @return bool Flag to indicate if any messages exist */ function message_get(&$messages, $user, $get_all = false, $sort = "ASC", $order_by = "ref") { switch ($order_by) { case "ref": $sql_order_by = "user_message.ref"; break; case "created": $sql_order_by = "message.created"; break; case "from": $sql_order_by = "owner"; break; case "fullname": $sql_order_by = "user.fullname"; break; case "message": $sql_order_by = "message.message"; break; case "expires": $sql_order_by = "message.expires"; break; case "seen": $sql_order_by = "user_message.seen"; break; } if (!validate_sort_value($sort)) { $sort = "ASC"; } $messages = ps_query("SELECT user_message.ref, message.ref AS 'message_id', user.username AS owner, user_message.seen, message.created, message.expires, message.message, message.url, message.owner as ownerid, message.type " . "FROM `user_message` INNER JOIN `message` ON user_message.message=message.ref " . "LEFT OUTER JOIN `user` ON message.owner=user.ref " . "WHERE user_message.user = ?" . ($get_all ? " " : " AND message.expires > NOW()") . ($get_all ? " " : " AND user_message.seen='0'") . " ORDER BY " . $sql_order_by . " " . $sort, array("i",$user)); return count($messages) > 0; } /** * Add a new resourcespace system message * * @param mixed $users User ID, or array of user IDs * @param string $text Message text * @param string $url URL to include as link in message * @param int $owner ID of message creator/owner * @param int $notification_type Message type e.g. MESSAGE_ENUM_NOTIFICATION_TYPE_SCREEN, MESSAGE_ENUM_NOTIFICATION_TYPE_EMAIL. See definitions.php * @param int $ttl_seconds Lifetime of message in seconds before expiry * @param int $related_activity ID of related activity type - see SYSTEM NOTIFICATION TYPES section in definitions.php * @param int $related_ref Related activity ID - used with type above to delete redundant messages e.g. once a user or resource request has been approved * @return void */ function message_add($users, $text, $url = "", $owner = null, $notification_type = MESSAGE_ENUM_NOTIFICATION_TYPE_SCREEN, $ttl_seconds = MESSAGE_DEFAULT_TTL_SECONDS, $related_activity = 0, $related_ref = 0) { global $userref,$applicationname,$lang, $baseurl, $baseurl_short,$header_colour_style_override; if (!is_int_loose($notification_type)) { $notification_type = intval($notification_type); // make sure this in an integer } $orig_text = $text; if (!is_array($users)) { $users = array($users); } if (checkperm('E')) { $validusers = get_users(0, "", "u.username", true); $validuserrefs = array_column($validusers, "ref"); $users = array_filter($users, function ($user) use ($validuserrefs) { return in_array($user, $validuserrefs); }); } if (is_null($owner) || (isset($userref) && $userref != $owner)) { // Can't send messages from another user $owner = $userref; } ps_query("INSERT INTO `message` (`owner`, `created`, `expires`, `message`, `url`, `related_activity`, `related_ref`, `type`) VALUES (? , NOW(), DATE_ADD(NOW(), INTERVAL ? SECOND), ?, ?, ?, ?, ?)", array("i",$owner,"i",$ttl_seconds,"s",$text,"s",str_replace($baseurl . '/', $baseurl_short, $url),"i",$related_activity,"i",$related_ref,"i",$notification_type)); $message_ref = sql_insert_id(); foreach ($users as $user) { ps_query("INSERT INTO `user_message` (`user`, `message`) VALUES (?, ?)", array("i",(int)$user,"i",$message_ref)); $user_info = get_user($user); // send an email if the user has notifications and emails setting and the message hasn't already been sent via email if (~$notification_type & MESSAGE_ENUM_NOTIFICATION_TYPE_EMAIL) { get_config_option(['user' => $user_info['ref'], 'usergroup' => $user_info['usergroup']], 'email_and_user_notifications', $notifications_always_email, $GLOBALS['system_wide_config_options']['email_and_user_notifications']); if ($notifications_always_email) { if ($user_info['email'] !== '') { if (substr($url, 0, 1) == "/") { // If a relative link is provided make sure we add the full URL when emailing $parsed_url = parse_url($baseurl); $url = $baseurl . (isset($parsed_url["path"]) ? str_replace($parsed_url["path"], "", $url) : $url); } $message_text = nl2br($orig_text); // Add system header image to email $headerimghtml = ""; $img_url = get_header_image(true); $img_div_style = "max-height:50px;padding: 5px;"; $img_div_style .= "background: " . ((isset($header_colour_style_override) && $header_colour_style_override != '') ? $header_colour_style_override : "rgba(0, 0, 0, 0.6)") . ";"; $headerimghtml = '


'; if ($url !== '' && strpos($message_text, $url) === false) { // Add the URL to the message if not already present $message_text = $message_text . "

" . $url . ""; } send_mail($user_info['email'], $applicationname . ": " . $lang['notification_email_subject'], $headerimghtml . $message_text); } } } } } /** * Remove a message from message table and associated user_messages * * @param int $message Message ID * @return void */ function message_remove($message) { ps_query("DELETE FROM user_message WHERE message = ?", array("i",$message)); ps_query("DELETE FROM message WHERE ref = ?", array("i",$message)); } /** * Mark a message as seen * * @param int $message Message ID * @param int $seen_type - see definitons.php * @return void */ function message_seen($message, $seen_type = MESSAGE_ENUM_NOTIFICATION_TYPE_SCREEN) { ps_query("UPDATE `user_message` SET seen = seen | ? WHERE `ref` = ?", array("i",$seen_type,"i",$message)); } /** * Mark a message as unseen * * @param int $message Message ID * @return void */ function message_unseen($message) { ps_query("UPDATE `user_message` SET seen = '0' WHERE `ref` = ?", array("i",$message)); } /** * Flags all non-read messages as read for given user and seen type * * @param int $user User ID * @param int $seen_type - see definitons.php * @return void */ function message_seen_all($user, $seen_type = MESSAGE_ENUM_NOTIFICATION_TYPE_SCREEN) { $messages = array(); if (message_get($messages, $user, true)) { foreach ($messages as $message) { message_seen($message['ref']); } } } /** * Remove all messages from message and user_message tables that have expired (regardless of read). * This will be called from a cron job. * * @return void */ function message_purge() { ps_query("DELETE FROM user_message WHERE message IN (SELECT ref FROM message where expires < NOW())", array()); ps_query("DELETE FROM message where expires < NOW()", array()); } /** * Delete all selected messages * * @param $messages List of message refs in JSON list format * @return void */ function message_deleteselusrmsg($messages) { global $userref; $parameters = array("i",(int)$userref); $messages = json_decode($messages, true); if (count($messages) > 0) { $parameters = array_merge($parameters, ps_param_fill($messages, "i")); ps_query("DELETE FROM user_message WHERE user = ? AND ref IN (" . ps_param_insert(count($messages)) . ")", $parameters); } } /** * Mark all selected messages as seen * * @param $messages List of message refs in JSON list format * @return void */ function message_selectedseen($messages) { global $userref; $parameters = array("i",(int)$userref); $messages = json_decode($messages, true); $parameters = array_merge($parameters, ps_param_fill($messages, "i")); if (is_array($messages) && count($messages) > 0) { ps_query("UPDATE user_message SET seen = '1' WHERE user = ? AND ref IN (" . ps_param_insert(count($messages)) . ")", $parameters); } } /** * Mark all selected messages as unseen * * @param $messages List of message refs in JSON list format * @return void */ function message_selectedunseen($messages) { global $userref; $parameters = array("i",(int)$userref); $messages = json_decode($messages, true); $parameters = array_merge($parameters, ps_param_fill($messages, "i")); if (is_array($messages) && count($messages) > 0) { ps_query("UPDATE user_message SET seen = '0' WHERE user = ? AND ref IN (" . ps_param_insert(count($messages)) . ")", $parameters); } } /** * Send a summary of all unread notifications as an email * from the standard cron_copy_hitcount * * @return boolean Returns false if not due to run */ function message_send_unread_emails() { global $lang, $applicationname, $baseurl, $list_search_results_title_trim, $user_pref_daily_digest, $applicationname, $actions_on, $inactive_message_auto_digest_period, $user_pref_inactive_digest; $lastrun = get_sysvar('daily_digest', '1970-01-01'); # Don't run if already run in last 24 hours. if (time() - strtotime($lastrun) < 24 * 60 * 60) { echo " - Skipping message_send_unread_emails (daily_digest) - last run: " . $lastrun . "
\n"; return false; } $sendall = array(); // Get all the users who have chosen to receive the digest email (or the ones that have opted out if set globally) if ($user_pref_daily_digest) { $allusers = get_users("", "", "u.username", "", -1, 1); $nodigestusers = get_config_option_users('user_pref_daily_digest', 0); $digestusers = array_diff(array_column($allusers, "ref"), $nodigestusers); } else { $digestusers = get_config_option_users('user_pref_daily_digest', 1); } if ($inactive_message_auto_digest_period > 0 && is_numeric($inactive_message_auto_digest_period)) { // Add any users who have not logged on to the array $allusers = get_users(0, "", "u.ref", false, -1, 1, false, "u.ref, u.username, u.last_active"); foreach ($allusers as $user) { if (!in_array($user["ref"], $digestusers) && strtotime((string)$user["last_active"]) < date(time() - $inactive_message_auto_digest_period * 60 * 60 * 24)) { debug("message_send_unread_emails: Processing unread messages for inactive user: " . $user["username"]); $digestusers[] = $user["ref"]; $sendall[] = $user["ref"]; } } } if (!empty($digestusers)) { $digestuserschunks = array_chunk($digestusers, SYSTEM_DATABASE_IDS_CHUNK_SIZE); $unreadmessages = []; foreach ($digestuserschunks as $chunk) { # Get all unread notifications created since last run, or all messages sent to inactive users. # Build array of sql query parameters $parameters = array(); $parameters = array_merge($parameters, ps_param_fill($chunk, "i")); $parameters = array_merge($parameters, array("s",$lastrun)); $digestusers_sql = " AND u.ref IN (" . ps_param_insert(count($chunk)) . ")"; $sendall_chunk = array_intersect($sendall, $chunk); if (count($sendall_chunk) > 0) { $parameters = array_merge($parameters, ps_param_fill($sendall_chunk, "i")); $sendall_sql = " OR u.ref IN (" . ps_param_insert(count($sendall_chunk)) . ")"; } else { $sendall_sql = ""; } $unreadmessages = array_merge( $unreadmessages, ps_query( "SELECT u.ref AS userref, u.email, m.ref AS messageref, m.message, m.created, m.url FROM user_message um JOIN user u ON u.ref = um.user JOIN message m ON m.ref = um.message WHERE um.seen = 0 $digestusers_sql AND u.email <> '' AND (m.created > ? $sendall_sql) ORDER BY m.created DESC", $parameters ) ); } } else { $parameters = array("s",$lastrun); $unreadmessages = ps_query( "SELECT u.ref AS userref, u.email, m.ref AS messageref, m.message, m.created, m.url FROM user_message um JOIN user u ON u.ref = um.user JOIN message m ON m.ref = um.message WHERE um.seen = 0 AND u.email <> '' AND (m.created > ?) ORDER BY m.created DESC", $parameters ); if (!empty($sendall)) { $sendall_chunks = array_chunk($sendall, SYSTEM_DATABASE_IDS_CHUNK_SIZE); foreach ($sendall_chunks as $sendall_chunk) { if (count($sendall_chunk) > 0) { $parameters_chunk = array_merge($parameters, ps_param_fill($sendall_chunk, "i")); $sendall_sql = " OR u.ref IN (" . ps_param_insert(count($sendall_chunk)) . ")"; } $unreadmessages = array_merge( $unreadmessages, ps_query( "SELECT u.ref AS userref, u.email, m.ref AS messageref, m.message, m.created, m.url FROM user_message um JOIN user u ON u.ref = um.user JOIN message m ON m.ref = um.message WHERE um.seen = 0 AND u.email <> '' AND (m.created > ? $sendall_sql) ORDER BY m.created DESC", $parameters_chunk ) ); } } } // Keep record of the current value for these config options. setup_user() may override them with the user group specific ones. $current_inactive_message_auto_digest_period = $inactive_message_auto_digest_period; $current_user_pref_inactive_digest = $user_pref_inactive_digest; $current_user_pref_daily_digest = $user_pref_daily_digest; foreach ($digestusers as $digestuser) { // Reset config variables before setting up the user to not have logic influenced by the previous iteration. $inactive_message_auto_digest_period = $current_inactive_message_auto_digest_period; $user_pref_inactive_digest = $current_user_pref_inactive_digest; $user_pref_daily_digest = $current_user_pref_daily_digest; $messageuser = get_user($digestuser); if (!$messageuser) { // Invalid user continue; } setup_user($messageuser); $pref_msg_user_for_inactive_digest = $pref_msg_user_pref_daily_digest = null; get_config_option(['user' => $messageuser['ref'], 'usergroup' => $messageuser['usergroup']], 'user_pref_inactive_digest', $pref_msg_user_for_inactive_digest); get_config_option(['user' => $messageuser['ref'], 'usergroup' => $messageuser['usergroup']], 'user_pref_daily_digest', $pref_msg_user_pref_daily_digest); if ($inactive_message_auto_digest_period == 0 || (!$pref_msg_user_for_inactive_digest && !$pref_msg_user_pref_daily_digest)) { debug("Skipping email digest for user ref " . $digestuser . " as user or group preference disabled"); continue; } $usermail = $messageuser["email"]; if (!filter_var($usermail, FILTER_VALIDATE_EMAIL)) { debug("Skipping email digest for user ref " . $digestuser . " due to invalid email: " . $usermail); continue; } $messageflag = false; $actionflag = false; // Set up an array of message to delete for this user if they have chosen to purge the messages $messagerefs = array(); // Start the new email if (in_array($digestuser, $sendall)) { $message = $lang['email_auto_digest_inactive'] . "

"; } else { $message = $lang['email_daily_digest_text'] . "

"; } $message .= ""; $message .= ""; foreach ($unreadmessages as $unreadmessage) { if ($unreadmessage["userref"] == $digestuser) { // Message applies to this user $messageflag = true; $usermail = $unreadmessage["email"]; $msgurl = $unreadmessage["url"]; if (substr($msgurl, 0, 1) == "/") { // If a relative link is provided make sure we add the full URL when emailing $msgurl = $baseurl . $msgurl; } $message .= ""; $messagerefs[] = $unreadmessage["messageref"]; } } if (count($messagerefs) == 0) { $message .= ""; } if ($actions_on) { if (!$actions_on) { break; } $user_actions = get_user_actions(false, '', 'date', 'DESC'); $action_count = count($user_actions); $actions_truncated = false; if ($action_count > 1000) { $user_actions = array_slice($user_actions, 0, 1000); $actions_truncated = true; } if ($action_count > 0) { $actionflag = true; debug("Adding actions to message for user " . $usermail); if ($messageflag) { $message .= "
" . $lang["columnheader-date_and_time"] . "" . $lang["message"] . "
" . nicedate($unreadmessage["created"], true, true, true) . "" . $unreadmessage["message"] . "" . $lang["link"] . "
" . $lang["nomessages"] . "


"; } $message .= $lang['email_daily_digest_actions'] . "

" . $lang["actions_introtext"] . "
"; if ($actions_truncated) { $message .= escape(str_replace(array('[total]', '[application_name]'), array($action_count, $applicationname), $lang['email_actions_truncated'])) . "
"; } $message .= ""; $message .= ""; $message .= ""; $message .= ""; $message .= ""; foreach ($user_actions as $user_action) { $actionlinks = hook("actioneditlink", '', array($user_action)); if ($actionlinks) { $actioneditlink = $actionlinks["editlink"]; $actionviewlink = $actionlinks["viewlink"]; } else { $actioneditlink = ''; $actionviewlink = ''; } if ($user_action["type"] == "resourcereview") { $actioneditlink = $baseurl . "/pages/edit.php"; $actionviewlink = $baseurl . "/pages/view.php"; } elseif ($user_action["type"] == "resourcerequest") { $actioneditlink = $baseurl . "/pages/team/team_request_edit.php"; } elseif ($user_action["type"] == "userrequest") { $actioneditlink = $baseurl . "/pages/team/team_user_edit.php"; } $linkparams["ref"] = $user_action["ref"]; $editlink = ($actioneditlink == '') ? '' : generateURL($actioneditlink, $linkparams); $viewlink = ($actionviewlink == '') ? '' : generateURL($actionviewlink, $linkparams); $message .= ""; $message .= ""; $message .= ""; $message .= ""; $message .= ""; $message .= ""; } // End of each $user_actions loop } } // Send the email debug("Sending summary to user ref " . $digestuser . ", email " . $usermail); $message .= "
" . $lang["date"] . "" . $lang["property-reference"] . "" . $lang["description"] . "" . $lang["type"] . "
" . nicedate($user_action["date"], true, true, true) . "" . $user_action["ref"] . "" . tidy_trim(TidyList($user_action["description"]), $list_search_results_title_trim) . "" . $lang["actions_type_" . $user_action["type"]] . "
"; if ($editlink != "") { $message .= "  " . $lang["action-edit"] . ""; } if ($viewlink != "") { $message .= "  " . $lang["view"] . ""; } $message .= "
"; $message .= "
"; $userprefurl = $baseurl . "/pages/user/user_preferences.php#UserPreferenceEmailSection"; $message .= "

" . $lang["email_digest_disable"] . "
" . $userprefurl . ""; if ($messageflag || $actionflag) { // Send mail send_mail($usermail, $applicationname . ": " . $lang["email_daily_digest_subject"], $message); } get_config_option(['user' => $messageuser['ref'], 'usergroup' => $messageuser['usergroup']], 'user_pref_daily_digest_mark_read', $mark_read); if ($mark_read && count($messagerefs) > 0) { $parameters = array("i",MESSAGE_ENUM_NOTIFICATION_TYPE_EMAIL); $parameters = array_merge($parameters, ps_param_fill($messagerefs, "i")); $parameters = array_merge($parameters, array("i",$digestuser)); ps_query("UPDATE user_message SET seen = ? WHERE message IN (" . ps_param_insert(count($messagerefs)) . ") and user = ?", $parameters); } } set_sysvar("daily_digest", date("Y-m-d H:i:s")); return true; } /** * Remove all messages related to a certain activity (e.g. resource request or resource submission) * matching the given ref(s) * @param int $remote_activity ID of related activity type - see SYSTEM NOTIFICATION TYPES section in definitions.php * @param mixed $remote_refs Related activity ID or array of IDs * @return void */ function message_remove_related($remote_activity = 0, $remote_refs = array()) { if ($remote_activity == 0 || $remote_refs == 0 || (is_array($remote_refs) && count($remote_refs) == 0)) { return false; } if (!is_array($remote_refs)) { $remote_refs = array($remote_refs); } $parameters = array("i", $remote_activity); $parameters = array_merge($parameters, ps_param_fill($remote_refs, "i")); $relatedmessages = ps_array("select ref value from message where related_activity = ? and related_ref in (" . ps_param_insert(count($remote_refs)) . ");", $parameters, ""); if (count($relatedmessages) > 0) { $parameters = ps_param_fill($relatedmessages, "i"); ps_query("DELETE FROM message WHERE ref in (" . ps_param_insert(count($relatedmessages)) . ");", $parameters); ps_query("DELETE FROM user_message WHERE message in (" . ps_param_insert(count($relatedmessages)) . ");", $parameters); } } /** * Send a system notification or email to the system administrators according to preference * * @param string $message Message text * @param string $url Optional URL * * @return void */ function system_notification($message, $url = "") { global $lang, $applicationname; $admin_notify_emails = array(); $admin_notify_users = array(); $notify_users = get_notification_users("SYSTEM_ADMIN"); $subject = str_replace("[application_name]", $applicationname, $lang["system_notification"]); foreach ($notify_users as $notify_user) { get_config_option(['user' => $notify_user['ref'], 'usergroup' => $notify_user['usergroup']], 'user_pref_system_management_notifications', $send_message); if (!$send_message) { continue; } get_config_option(['user' => $notify_user['ref'], 'usergroup' => $notify_user['usergroup']], 'email_user_notifications', $send_email); if ($send_email && $notify_user["email"] != "") { $admin_notify_emails[] = $notify_user['email']; } else { $admin_notify_users[] = $notify_user["ref"]; } } foreach ($admin_notify_emails as $admin_notify_email) { $template = "system_notification_email"; $templatevars = array("message" => $message,"url" => $url); $messageplain = $message . "\n\n" . $url; send_mail($admin_notify_email, $subject, $messageplain, '', '', $template, $templatevars); } if (count($admin_notify_users) > 0) { message_add($admin_notify_users, $message, $url, 0); } } /** * Get all message refs for a given user * * @param string $user User ID * * @return void */ function message_getrefs($user) { $user_messages = ps_query("SELECT ref FROM user_message WHERE user = ?", array("i",$user)); $js_array = array_values($user_messages); echo json_encode($js_array); } /** * Get all messages between the given user IDs * * @param int $users User ID * @param array $msgusers Array of other user IDs * * @param array $filteropts Array of extra options to filter and sort messages returned * "msgfind" - (string) Text to find * "sort_desc" - (bool) Sort by message ID in descending order? False = Ascending * "limit" - (int) Maximum number of messages to return * * @return array Array of messages */ function message_get_conversation(int $user, $msgusers = array(), $filteropts = array()): array { array_map("is_int_loose", $msgusers); if (count($msgusers) == 0 || !is_int_loose($user)) { return array(); } $msgfind = $filteropts['msgfind'] ?? ''; $sort_desc = $filteropts['sort_desc'] ?? false; $limit = $filteropts['limit'] ?? 0; # Build array of sql query parameters $parameters = ps_param_fill($msgusers, "i"); $parameters = array_merge($parameters, array("i",$user), array("i",$user)); $parameters = array_merge($parameters, ps_param_fill($msgusers, "i")); if ($msgfind != "") { $parameters = array_merge($parameters, array("s", $msgfind)); } if ($limit > 0) { $parameters = array_merge($parameters, array("i", (int)$limit)); } $msgquery = "SELECT message.created, message.owner, message.message, message.url, message.expires, message.type, user_message.user, user_message.ref, user_message.seen FROM message LEFT JOIN user_message ON user_message.message=message.ref WHERE ((owner IN(" . ps_param_insert(count($msgusers)) . ") AND user_message.user = ?) OR (owner = ? AND user_message.user IN(" . ps_param_insert(count($msgusers)) . ")))" . ($msgfind != "" ? (" AND message.message LIKE ?") : " " ) . " AND type & '" . MESSAGE_ENUM_NOTIFICATION_TYPE_USER_MESSAGE . "'" . " ORDER BY user_message.ref " . ($sort_desc ? "DESC" : "ASC") . ($limit > 0 ? " LIMIT ?" : ""); return ps_query($msgquery, $parameters); } /** * Send a user to user(s) message * * @param array $users Array of user IDs or usernames/groupnames from user select * @param string $text Message text * @return bool|string True if sent ok or error message */ function send_user_message($users, $text) { global $userref, $lang; $users = remove_groups_smart_from_userlist($users); $users = explode(",", resolve_userlist_groups($users)); for ($n = 0; $n < count($users); $n++) { if (!is_int_loose($users[$n])) { $uref = get_user_by_username(trim($users[$n])); if (!$uref) { return $lang["error_invalid_user"]; } $users[$n] = $uref; } } message_add($users, $text, "", $userref, MESSAGE_ENUM_NOTIFICATION_TYPE_USER_MESSAGE + MESSAGE_ENUM_NOTIFICATION_TYPE_SCREEN, 30 * 24 * 60 * 60); return true; } /** * Send system notifications to specified users, checking the user preferences first if specified * * @param array $users Array of user IDs or array of user details from get_users() * @param ResourceSpaceUserNotification $notifymessage An instance of a ResourceSpaceUserNotification object holding message properties * @param bool $forcemail Force system to send email instead of notification? * * @return array Array containing resulting messages - can be used for testing when emails are not being sent * This will contain two arrays:- * "emails" array of emails sent, with the following elements:- * "email" => Email address * "subject" => Email subject * "body" => Body text * * "messages" Array of system messages sent with the following elements :- * "user" => User ID * "message" => message text * "url" => url */ function send_user_notification(array $users, $notifymessage, $forcemail = false) { global $userref, $lang, $plugins, $header_colour_style_override; // Need to global $applicationname as it is used inside the lang files global $applicationname; $userlanguages = []; // This stores the users in their relevant language key element // Set up $results array $results = []; $results["messages"] = []; $results["emails"] = []; foreach ($users as $notify_user) { $userdetails = $notify_user; if (!is_array($userdetails)) { $userdetails = get_user((int)$userdetails); } elseif (!isset($userdetails["lang"])) { // Need full user info $userdetails = get_user($userdetails["ref"]); } if (!$userdetails) { continue; } $send_message = true; // Check if preferences should prevent the notification from being sent if (isset($notifymessage->user_preference) && is_array($notifymessage->user_preference)) { foreach ($notifymessage->user_preference as $preference => $vals) { if ($preference != "") { $requiredvalue = (bool)$vals["requiredvalue"]; $default = (bool)$vals["default"]; get_config_option(['user' => $userdetails['ref'], 'usergroup' => $userdetails['usergroup']], $preference, $check_pref, $default); debug(" - Required preference: " . $preference . " = " . ($requiredvalue ? "TRUE" : "FALSE")); debug(" - User preference value: " . $preference . " = " . ($check_pref ? "TRUE" : "FALSE")); if ($check_pref != $requiredvalue) { debug("Skipping notification to user #" . $userdetails['ref']); $send_message = false; } } } } if (!$send_message) { continue; } debug("Sending notification to user #" . $userdetails["ref"]); get_config_option(['user' => $userdetails['ref'], 'usergroup' => $userdetails['usergroup']], 'email_user_notifications', $send_email); if (!isset($userlanguages[$userdetails['lang']])) { $userlanguages[$userdetails['lang']] = []; $userlanguages[$userdetails['lang']]["emails"] = []; $userlanguages[$userdetails['lang']]["message_users"] = []; } if (($send_email && filter_var($userdetails["email"], FILTER_VALIDATE_EMAIL)) || $forcemail) { debug("Sending email to user #" . $userdetails["ref"]); $userlanguages[$userdetails['lang']]["emails"][] = $userdetails["email"]; } else { debug("Sending system message to user #" . $userdetails["ref"]); $userlanguages[$userdetails['lang']]["message_users"][] = $userdetails["ref"]; } } $url = $notifymessage->url ?? null; $headerimghtml = ""; if (!isset($notifymessage->template)) { // Add header image to email if not using template $img_url = get_header_image(true); $img_div_style = 'float: left;width: 100%;max-height:50px;padding: 5px;'; $img_div_style .= "background: " . ((isset($header_colour_style_override) && $header_colour_style_override != '') ? $header_colour_style_override : "#fff") . ";"; $headerimghtml .= '
'; $headerimghtml .= '
'; $headerimghtml .= '
'; $headerimghtml .= ''; $headerimghtml .= '


'; } foreach ($userlanguages as $userlanguage => $notifications) { debug("Processing notifications for language: '" . $userlanguage . "'"); // Save the current lang array $saved_lang = $lang; if ($userlanguage != "en") { if (substr($userlanguage, 2, 1) == '-' && substr($userlanguage, 0, 2) != 'en') { $langpath = __DIR__ . "/../languages/" . safe_file_name(substr($userlanguage, 0, 2)) . ".php"; if (file_exists($langpath)) { include $langpath; } } $langpath = __DIR__ . "/../languages/" . safe_file_name($userlanguage) . ".php"; if (file_exists($langpath)) { include $langpath; } } # Register plugin languages in reverse order for ($n = count($plugins) - 1; $n >= 0; $n--) { if (!isset($plugins[$n])) { continue; } register_plugin_language($plugins[$n]); } // Load in the correct language strings lang_load_site_text($lang, "", $userlanguage); $subject = $notifymessage->get_subject(); $messagetext = $notifymessage->get_text(); if (count($notifications["message_users"]) > 0) { $activitytype = $notifymessage->eventdata["type"] ?? null; $relatedactivity = $notifymessage->eventdata["ref"] ?? null; foreach ($notifications["message_users"] as $notifyuser) { $results["messages"][] = ["user" => $notifyuser,"message" => $messagetext,"url" => $url]; } message_add($notifications["message_users"], $messagetext, (string) $url, $userref, MESSAGE_ENUM_NOTIFICATION_TYPE_SCREEN, MESSAGE_DEFAULT_TTL_SECONDS, $activitytype, $relatedactivity); } if (count($notifications["emails"]) > 0) { if (!empty($url) && !is_null($url) && strpos($messagetext, $url) === false) { // Add the URL to the message if not already present $messagetext = $messagetext . "

" . $url . ""; } foreach ($notifications["emails"] as $emailrecipient) { send_mail($emailrecipient, $subject, $headerimghtml . $messagetext, "", "", $notifymessage->template, $notifymessage->templatevars); $results["emails"][] = ["email" => $emailrecipient,"subject" => $subject,"body" => $headerimghtml . $messagetext]; } foreach ($notifications["message_users"] as $notifyuser) { $results[$notifyuser] = ["type" => "messsage","body" => $messagetext]; } } // Restore the saved $lang array $lang = $saved_lang; } return $results; } /** * Gets the user message for the given ref * * @param int $ref Message ID * @param bool $checkaccess Check if user can see the given message? * * @return array|bool Array with two elements: 'message' => message text,'url'=> message URL and 'owner' => message owner * False if user has no access or the requested message doesn't exist, */ function get_user_message(int $ref, bool $checkaccess = true) { global $userref; if ($checkaccess) { $validmessages = ps_array("SELECT `message` value FROM user_message WHERE user = ?", ["i",$userref]); if (!in_array($ref, $validmessages)) { return false; } } $message = ps_query("SELECT message, url, owner FROM message WHERE ref = ?", ["i",$ref]); return $message ? ["message" => $message[0]["message"],"url" => $message[0]["url"],"owner" => $message[0]["owner"]] : false; } /** * Send notifications about file integrity failures * * @param array $failures Array of resources that have failed integrity check * */ function send_integrity_failure_notices(array $failures): void { $last_integrity_check_notify = get_sysvar('last_integrity_check_notify', '1970-01-01'); if (time() - strtotime($last_integrity_check_notify) < 24 * 60 * 60) { if ('cli' == PHP_SAPI) { echo " - Skipping sending of integrity failure notifications - last sent: " . $last_integrity_check_notify . PHP_EOL; } } else { if ('cli' == PHP_SAPI) { echo " - Sending summary messages to administrators" . PHP_EOL; } # Send notification $message = new ResourceSpaceUserNotification(); $message->set_subject("lang_file_integrity_summary"); $message->set_text("lang_file_integrity_summary_failed"); $message->append_text("

"); if (count($failures) < 1000) { // Show links to the resources that have failed this time. // If too many have failed it will still include a link to the general search for all failed resources. $message->append_text("append_text(""); $n++; } $message->append_text("
"); $message->append_text("lang_file_integrity_fail_search"); $message->append_text(" ({$n})"); $message->append_text("
"); } $message->url = $GLOBALS["baseurl"] . "/pages/search.php?search=!integrityfail"; $notify_users = get_notification_users("SYSTEM_ADMIN"); send_user_notification($notify_users, $message); set_sysvar("last_integrity_check_notify", date("Y-m-d H:i:s")); } } /** * Limit the length of !list special search url by adding a maximum of 650 characters of resource references per link including separators. * Mail servers may break very long text strings into multiple lines and this will cause the special search to fail. * Loading URLs of great length may also cause a Request-URI Too Long error in the browser. * Multiple urls will be returned, formatted to include in action dates notifications. * * @param array $resource_refs Array containing resource references to include in url. * * @return array Array containing 'single' value of url (650 characters of resources or less) and 'multiple' value of url (more than 650 characters of resources). */ function build_specialsearch_list_urls(array $resource_refs) { global $baseurl, $lang; $return_urls['single'] = ''; // Two values returned to determine where the url is placed in message_add() - multiple urls must be passed in message, not url parameter. $return_urls['multiple'] = ''; if (count($resource_refs) === 0) { return $return_urls; } $total_string_length = 0; $link = 1; $resource_links = array(); foreach ($resource_refs as $cur_ref) { $cur_ref_length = strlen($cur_ref) + 1; // +1 represents separator ':' $total_string_length = $total_string_length + $cur_ref_length; if ($total_string_length > 650) { $total_string_length = $cur_ref_length; $link = ++$link; } $resource_links[$link][] = $cur_ref; } if (count($resource_links) === 1) { $return_urls['single'] = "$baseurl/pages/search.php?search=!list" . implode(":", $resource_refs); } else { $urls = array('
'; $return_urls['multiple'] = implode("", $urls); } return $return_urls; }