示例#1
0
/**
 * A special function for handling the hell which is sending approval notifications.
 *
 * @param mixed[] $topicData
 */
function sendApprovalNotifications(&$topicData)
{
    global $scripturl, $language, $user_info, $modSettings;
    $db = database();
    // Clean up the data...
    if (!is_array($topicData) || empty($topicData)) {
        return;
    }
    // Email ahoy
    require_once SUBSDIR . '/Mail.subs.php';
    require_once SUBSDIR . '/Emailpost.subs.php';
    $topics = array();
    $digest_insert = array();
    foreach ($topicData as $topic => $msgs) {
        foreach ($msgs as $msgKey => $msg) {
            // Convert it to markdown for sending, censor is done as well
            pbe_prepare_text($topicData[$topic][$msgKey]['body'], $topicData[$topic][$msgKey]['subject']);
            $topics[] = $msg['id'];
            $digest_insert[] = array($msg['topic'], $msg['id'], 'reply', $user_info['id']);
        }
    }
    // These need to go into the digest too...
    $db->insert('', '{db_prefix}log_digest', array('id_topic' => 'int', 'id_msg' => 'int', 'note_type' => 'string', 'exclude' => 'int'), $digest_insert, array());
    // Find everyone who needs to know about this.
    $members = $db->query('', '
		SELECT
			mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.lngfile,
			ln.sent, mem.id_group, mem.additional_groups, b.member_groups, mem.id_post_group, t.id_member_started,
			ln.id_topic
		FROM {db_prefix}log_notify AS ln
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic)
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
		WHERE ln.id_topic IN ({array_int:topic_list})
			AND mem.is_activated = {int:is_activated}
			AND mem.notify_types < {int:notify_types}
			AND mem.notify_regularity < {int:notify_regularity}
		GROUP BY mem.id_member, ln.id_topic, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.lngfile, ln.sent, mem.id_group, mem.additional_groups, b.member_groups, mem.id_post_group, t.id_member_started
		ORDER BY mem.lngfile', array('topic_list' => $topics, 'is_activated' => 1, 'notify_types' => 4, 'notify_regularity' => 2));
    $sent = 0;
    $current_language = $user_info['language'];
    while ($row = $db->fetch_assoc($members)) {
        if ($row['id_group'] != 1) {
            $allowed = explode(',', $row['member_groups']);
            $row['additional_groups'] = explode(',', $row['additional_groups']);
            $row['additional_groups'][] = $row['id_group'];
            $row['additional_groups'][] = $row['id_post_group'];
            if (count(array_intersect($allowed, $row['additional_groups'])) == 0) {
                continue;
            }
        }
        $needed_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'];
        if (empty($current_language) || $current_language != $needed_language) {
            $current_language = loadLanguage('Post', $needed_language, false);
        }
        $sent_this_time = false;
        $replacements = array('TOPICLINK' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new', 'UNSUBSCRIBELINK' => $scripturl . '?action=notify;topic=' . $row['id_topic'] . '.0');
        // Now loop through all the messages to send.
        foreach ($topicData[$row['id_topic']] as $msg) {
            $replacements += array('TOPICSUBJECT' => $msg['subject'], 'POSTERNAME' => un_htmlspecialchars($msg['name']));
            $message_type = 'notification_reply';
            // Do they want the body of the message sent too?
            if (!empty($row['notify_send_body']) && empty($modSettings['disallow_sendBody'])) {
                $message_type .= '_body';
                $replacements['MESSAGE'] = $msg['body'];
            }
            if (!empty($row['notify_regularity'])) {
                $message_type .= '_once';
            }
            // Send only if once is off or it's on and it hasn't been sent.
            if (empty($row['notify_regularity']) || empty($row['sent']) && !$sent_this_time) {
                $emaildata = loadEmailTemplate($message_type, $replacements, $needed_language);
                sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, 'm' . $msg['last_id']);
                $sent++;
            }
            $sent_this_time = true;
        }
    }
    $db->free_result($members);
    if (isset($current_language) && $current_language != $user_info['language']) {
        loadLanguage('Post');
    }
    // Sent!
    if (!empty($sent)) {
        $db->query('', '
			UPDATE {db_prefix}log_notify
			SET sent = {int:is_sent}
			WHERE id_topic IN ({array_int:topic_list})
				AND id_member != {int:current_member}', array('current_member' => $user_info['id'], 'topic_list' => $topics, 'is_sent' => 1));
    }
}
示例#2
0
 /**
  * Send the announcement in chunks.
  *
  * splits the members to be sent a topic announcement into chunks.
  * composes notification messages in all languages needed.
  * does the actual sending of the topic announcements in chunks.
  * calculates a rough estimate of the percentage items sent.
  * Accessed by action=announce;sa=send
  * @uses announcement template announcement_send sub template
  */
 public function action_send()
 {
     global $topic, $board, $board_info, $context, $modSettings, $language, $scripturl;
     checkSession();
     $context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start'];
     $groups = array_merge($board_info['groups'], array(1));
     $who = array();
     // Load any supplied membergroups (from announcement_send template pause loop)
     if (isset($_POST['membergroups'])) {
         $_POST['who'] = explode(',', $_POST['membergroups']);
     }
     // Check that at least one membergroup was selected (set from announce sub template)
     if (empty($_POST['who'])) {
         fatal_lang_error('no_membergroup_selected');
     }
     // Make sure all membergroups are integers and can access the board of the announcement.
     foreach ($_POST['who'] as $id => $mg) {
         $who[$id] = in_array((int) $mg, $groups) ? (int) $mg : 0;
     }
     // Get the topic details that we are going to send
     require_once SUBSDIR . '/Topic.subs.php';
     $topic_info = getTopicInfo($topic, 'message');
     // Prepare a plain text (markdown) body for email use, does the censoring as well
     require_once SUBSDIR . '/Emailpost.subs.php';
     pbe_prepare_text($topic_info['body'], $topic_info['subject']);
     // We need this in order to be able send emails.
     require_once SUBSDIR . '/Mail.subs.php';
     require_once SUBSDIR . '/Members.subs.php';
     // Select the email addresses for this batch.
     $conditions = array('activated_status' => 1, 'member_greater' => $context['start'], 'group_list' => $who, 'order_by' => 'id_member', 'limit' => empty($modSettings['mail_queue']) ? 25 : 500);
     // Have we allowed members to opt out of announcements?
     if (!empty($modSettings['allow_disableAnnounce'])) {
         $conditions['notify_announcements'] = 1;
     }
     $data = retrieveMemberData($conditions);
     // All members have received a mail. Go to the next screen.
     if (empty($data) || $data['member_count'] === 0) {
         logAction('announce_topic', array('topic' => $topic), 'user');
         if (!empty($_REQUEST['move']) && allowedTo('move_any')) {
             redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
         } elseif (!empty($_REQUEST['goback'])) {
             redirectexit('topic=' . $topic . '.new;boardseen#new', isBrowser('ie'));
         } else {
             redirectexit('board=' . $board . '.0');
         }
     }
     // Loop through all members that'll receive an announcement in this batch.
     $announcements = array();
     foreach ($data['member_info'] as $row) {
         $cur_language = empty($row['language']) || empty($modSettings['userLanguage']) ? $language : $row['language'];
         // If the language wasn't defined yet, load it and compose a notification message.
         if (!isset($announcements[$cur_language])) {
             $replacements = array('TOPICSUBJECT' => $topic_info['subject'], 'MESSAGE' => $topic_info['body'], 'TOPICLINK' => $scripturl . '?topic=' . $topic . '.0');
             $emaildata = loadEmailTemplate('new_announcement', $replacements, $cur_language);
             $announcements[$cur_language] = array('subject' => $emaildata['subject'], 'body' => $emaildata['body'], 'recipients' => array());
         }
         $announcements[$cur_language]['recipients'][$row['id']] = $row['email'];
         $context['start'] = $row['id'];
     }
     // For each language send a different mail - low priority...
     foreach ($announcements as $lang => $mail) {
         sendmail($mail['recipients'], $mail['subject'], $mail['body'], null, null, false, 5);
     }
     // Provide an overall indication of progress, this is not strictly correct
     if ($data['member_count'] < $conditions['limit']) {
         $context['percentage_done'] = 100;
     } else {
         $context['percentage_done'] = round(100 * $context['start'] / $modSettings['latestMember'], 1);
     }
     // Prepare for the template
     $context['move'] = empty($_REQUEST['move']) ? 0 : 1;
     $context['go_back'] = empty($_REQUEST['goback']) ? 0 : 1;
     $context['membergroups'] = implode(',', $who);
     $context['topic_subject'] = $topic_info['subject'];
     $context['sub_template'] = 'announcement_send';
     // Go back to the correct language for the user ;)
     if (!empty($modSettings['userLanguage'])) {
         loadLanguage('Post');
     }
 }
/**
 * Sends a personal message from the specified person to the specified people
 * ($from defaults to the user)
 *
 * @package PersonalMessage
 * @param mixed[] $recipients - an array containing the arrays 'to' and 'bcc', both containing id_member's.
 * @param string $subject - should have no slashes and no html entities
 * @param string $message - should have no slashes and no html entities
 * @param bool $store_outbox
 * @param mixed[]|null $from - an array with the id, name, and username of the member.
 * @param int $pm_head - the ID of the chain being replied to - if any.
 * @return mixed[] an array with log entries telling how many recipients were successful and which recipients it failed to send to.
 */
function sendpm($recipients, $subject, $message, $store_outbox = true, $from = null, $pm_head = 0)
{
    global $scripturl, $txt, $user_info, $language, $modSettings, $webmaster_email;
    $db = database();
    // Make sure the PM language file is loaded, we might need something out of it.
    loadLanguage('PersonalMessage');
    // Needed for our email and post functions
    require_once SUBSDIR . '/Mail.subs.php';
    require_once SUBSDIR . '/Post.subs.php';
    // Initialize log array.
    $log = array('failed' => array(), 'sent' => array());
    if ($from === null) {
        $from = array('id' => $user_info['id'], 'name' => $user_info['name'], 'username' => $user_info['username']);
    } else {
        $user_info['name'] = $from['name'];
    }
    // This is the one that will go in their inbox.
    $htmlmessage = Util::htmlspecialchars($message, ENT_QUOTES, 'UTF-8', true);
    preparsecode($htmlmessage);
    $htmlsubject = strtr(Util::htmlspecialchars($subject), array("\r" => '', "\n" => '', "\t" => ''));
    if (Util::strlen($htmlsubject) > 100) {
        $htmlsubject = Util::substr($htmlsubject, 0, 100);
    }
    // Make sure is an array
    if (!is_array($recipients)) {
        $recipients = array($recipients);
    }
    // Integrated PMs
    call_integration_hook('integrate_personal_message', array(&$recipients, &$from, &$subject, &$message));
    // Get a list of usernames and convert them to IDs.
    $usernames = array();
    foreach ($recipients as $rec_type => $rec) {
        foreach ($rec as $id => $member) {
            if (!is_numeric($recipients[$rec_type][$id])) {
                $recipients[$rec_type][$id] = Util::strtolower(trim(preg_replace('/[<>&"\'=\\\\]/', '', $recipients[$rec_type][$id])));
                $usernames[$recipients[$rec_type][$id]] = 0;
            }
        }
    }
    if (!empty($usernames)) {
        $request = $db->query('pm_find_username', '
			SELECT
				id_member, member_name
			FROM {db_prefix}members
			WHERE ' . (defined('DB_CASE_SENSITIVE') ? 'LOWER(member_name)' : 'member_name') . ' IN ({array_string:usernames})', array('usernames' => array_keys($usernames)));
        while ($row = $db->fetch_assoc($request)) {
            if (isset($usernames[Util::strtolower($row['member_name'])])) {
                $usernames[Util::strtolower($row['member_name'])] = $row['id_member'];
            }
        }
        $db->free_result($request);
        // Replace the usernames with IDs. Drop usernames that couldn't be found.
        foreach ($recipients as $rec_type => $rec) {
            foreach ($rec as $id => $member) {
                if (is_numeric($recipients[$rec_type][$id])) {
                    continue;
                }
                if (!empty($usernames[$member])) {
                    $recipients[$rec_type][$id] = $usernames[$member];
                } else {
                    $log['failed'][$id] = sprintf($txt['pm_error_user_not_found'], $recipients[$rec_type][$id]);
                    unset($recipients[$rec_type][$id]);
                }
            }
        }
    }
    // Make sure there are no duplicate 'to' members.
    $recipients['to'] = array_unique($recipients['to']);
    // Only 'bcc' members that aren't already in 'to'.
    $recipients['bcc'] = array_diff(array_unique($recipients['bcc']), $recipients['to']);
    // Combine 'to' and 'bcc' recipients.
    $all_to = array_merge($recipients['to'], $recipients['bcc']);
    // Check no-one will want it deleted right away!
    $request = $db->query('', '
		SELECT
			id_member, criteria, is_or
		FROM {db_prefix}pm_rules
		WHERE id_member IN ({array_int:to_members})
			AND delete_pm = {int:delete_pm}', array('to_members' => $all_to, 'delete_pm' => 1));
    $deletes = array();
    // Check whether we have to apply anything...
    while ($row = $db->fetch_assoc($request)) {
        $criteria = unserialize($row['criteria']);
        // Note we don't check the buddy status, cause deletion from buddy = madness!
        $delete = false;
        foreach ($criteria as $criterium) {
            if ($criterium['t'] == 'mid' && $criterium['v'] == $from['id'] || $criterium['t'] == 'gid' && in_array($criterium['v'], $user_info['groups']) || $criterium['t'] == 'sub' && strpos($subject, $criterium['v']) !== false || $criterium['t'] == 'msg' && strpos($message, $criterium['v']) !== false) {
                $delete = true;
            } elseif (!$row['is_or']) {
                $delete = false;
                break;
            }
        }
        if ($delete) {
            $deletes[$row['id_member']] = 1;
        }
    }
    $db->free_result($request);
    // Load the membergrounp message limits.
    static $message_limit_cache = array();
    if (!allowedTo('moderate_forum') && empty($message_limit_cache)) {
        $request = $db->query('', '
			SELECT
				id_group, max_messages
			FROM {db_prefix}membergroups', array());
        while ($row = $db->fetch_assoc($request)) {
            $message_limit_cache[$row['id_group']] = $row['max_messages'];
        }
        $db->free_result($request);
    }
    // Load the groups that are allowed to read PMs.
    // @todo move into a separate function on $permission.
    $allowed_groups = array();
    $disallowed_groups = array();
    $request = $db->query('', '
		SELECT
			id_group, add_deny
		FROM {db_prefix}permissions
		WHERE permission = {string:read_permission}', array('read_permission' => 'pm_read'));
    while ($row = $db->fetch_assoc($request)) {
        if (empty($row['add_deny'])) {
            $disallowed_groups[] = $row['id_group'];
        } else {
            $allowed_groups[] = $row['id_group'];
        }
    }
    $db->free_result($request);
    if (empty($modSettings['permission_enable_deny'])) {
        $disallowed_groups = array();
    }
    $request = $db->query('', '
		SELECT
			member_name, real_name, id_member, email_address, lngfile,
			pm_email_notify, personal_messages,' . (allowedTo('moderate_forum') ? ' 0' : '
			(receive_from = {int:admins_only}' . (empty($modSettings['enable_buddylist']) ? '' : ' OR
			(receive_from = {int:buddies_only} AND FIND_IN_SET({string:from_id}, buddy_list) = 0) OR
			(receive_from = {int:not_on_ignore_list} AND FIND_IN_SET({string:from_id}, pm_ignore_list) != 0)') . ')') . ' AS ignored,
			FIND_IN_SET({string:from_id}, buddy_list) != 0 AS is_buddy, is_activated,
			additional_groups, id_group, id_post_group
		FROM {db_prefix}members
		WHERE id_member IN ({array_int:recipients})
		ORDER BY lngfile
		LIMIT {int:count_recipients}', array('not_on_ignore_list' => 1, 'buddies_only' => 2, 'admins_only' => 3, 'recipients' => $all_to, 'count_recipients' => count($all_to), 'from_id' => $from['id']));
    $notifications = array();
    while ($row = $db->fetch_assoc($request)) {
        // Don't do anything for members to be deleted!
        if (isset($deletes[$row['id_member']])) {
            continue;
        }
        // We need to know this members groups.
        $groups = explode(',', $row['additional_groups']);
        $groups[] = $row['id_group'];
        $groups[] = $row['id_post_group'];
        $message_limit = -1;
        // For each group see whether they've gone over their limit - assuming they're not an admin.
        if (!in_array(1, $groups)) {
            foreach ($groups as $id) {
                if (isset($message_limit_cache[$id]) && $message_limit != 0 && $message_limit < $message_limit_cache[$id]) {
                    $message_limit = $message_limit_cache[$id];
                }
            }
            if ($message_limit > 0 && $message_limit <= $row['personal_messages']) {
                $log['failed'][$row['id_member']] = sprintf($txt['pm_error_data_limit_reached'], $row['real_name']);
                unset($all_to[array_search($row['id_member'], $all_to)]);
                continue;
            }
            // Do they have any of the allowed groups?
            if (count(array_intersect($allowed_groups, $groups)) == 0 || count(array_intersect($disallowed_groups, $groups)) != 0) {
                $log['failed'][$row['id_member']] = sprintf($txt['pm_error_user_cannot_read'], $row['real_name']);
                unset($all_to[array_search($row['id_member'], $all_to)]);
                continue;
            }
        }
        // Note that PostgreSQL can return a lowercase t/f for FIND_IN_SET
        if (!empty($row['ignored']) && $row['ignored'] != 'f' && $row['id_member'] != $from['id']) {
            $log['failed'][$row['id_member']] = sprintf($txt['pm_error_ignored_by_user'], $row['real_name']);
            unset($all_to[array_search($row['id_member'], $all_to)]);
            continue;
        }
        // If the receiving account is banned (>=10) or pending deletion (4), refuse to send the PM.
        if ($row['is_activated'] >= 10 || $row['is_activated'] == 4 && !$user_info['is_admin']) {
            $log['failed'][$row['id_member']] = sprintf($txt['pm_error_user_cannot_read'], $row['real_name']);
            unset($all_to[array_search($row['id_member'], $all_to)]);
            continue;
        }
        // Send a notification, if enabled - taking the buddy list into account.
        if (!empty($row['email_address']) && ($row['pm_email_notify'] == 1 || $row['pm_email_notify'] > 1 && (!empty($modSettings['enable_buddylist']) && $row['is_buddy'])) && $row['is_activated'] == 1) {
            $notifications[empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']][] = $row['email_address'];
        }
        $log['sent'][$row['id_member']] = sprintf(isset($txt['pm_successfully_sent']) ? $txt['pm_successfully_sent'] : '', $row['real_name']);
    }
    $db->free_result($request);
    // Only 'send' the message if there are any recipients left.
    if (empty($all_to)) {
        return $log;
    }
    // Track the pm count for our stats
    if (!empty($modSettings['trackStats'])) {
        trackStats(array('pm' => '+'));
    }
    // Insert the message itself and then grab the last insert id.
    $db->insert('', '{db_prefix}personal_messages', array('id_pm_head' => 'int', 'id_member_from' => 'int', 'deleted_by_sender' => 'int', 'from_name' => 'string-255', 'msgtime' => 'int', 'subject' => 'string-255', 'body' => 'string-65534'), array($pm_head, $from['id'], $store_outbox ? 0 : 1, $from['username'], time(), $htmlsubject, $htmlmessage), array('id_pm'));
    $id_pm = $db->insert_id('{db_prefix}personal_messages', 'id_pm');
    // Add the recipients.
    if (!empty($id_pm)) {
        // If this is new we need to set it part of it's own conversation.
        if (empty($pm_head)) {
            $db->query('', '
				UPDATE {db_prefix}personal_messages
				SET id_pm_head = {int:id_pm_head}
				WHERE id_pm = {int:id_pm_head}', array('id_pm_head' => $id_pm));
        }
        // Some people think manually deleting personal_messages is fun... it's not. We protect against it though :)
        $db->query('', '
			DELETE FROM {db_prefix}pm_recipients
			WHERE id_pm = {int:id_pm}', array('id_pm' => $id_pm));
        $insertRows = array();
        $to_list = array();
        foreach ($all_to as $to) {
            $insertRows[] = array($id_pm, $to, in_array($to, $recipients['bcc']) ? 1 : 0, isset($deletes[$to]) ? 1 : 0, 1);
            if (!in_array($to, $recipients['bcc'])) {
                $to_list[] = $to;
            }
        }
        $db->insert('insert', '{db_prefix}pm_recipients', array('id_pm' => 'int', 'id_member' => 'int', 'bcc' => 'int', 'deleted' => 'int', 'is_new' => 'int'), $insertRows, array('id_pm', 'id_member'));
    }
    $maillist = !empty($modSettings['maillist_enabled']) && !empty($modSettings['pbe_pm_enabled']);
    // If they have post by email enabled, override disallow_sendBody
    if (!$maillist && !empty($modSettings['disallow_sendBody'])) {
        $message = '';
        censorText($subject);
    } else {
        require_once SUBSDIR . '/Emailpost.subs.php';
        pbe_prepare_text($message, $subject);
    }
    $to_names = array();
    if (count($to_list) > 1) {
        require_once SUBSDIR . '/Members.subs.php';
        $result = getBasicMemberData($to_list);
        foreach ($result as $row) {
            $to_names[] = un_htmlspecialchars($row['real_name']);
        }
    }
    $replacements = array('SUBJECT' => $subject, 'MESSAGE' => $message, 'SENDER' => un_htmlspecialchars($from['name']), 'READLINK' => $scripturl . '?action=pm;pmsg=' . $id_pm . '#msg' . $id_pm, 'REPLYLINK' => $scripturl . '?action=pm;sa=send;f=inbox;pmsg=' . $id_pm . ';quote;u=' . $from['id'], 'TOLIST' => implode(', ', $to_names));
    // Select the right template
    $email_template = ($maillist && empty($modSettings['disallow_sendBody']) ? 'pbe_' : '') . 'new_pm' . (empty($modSettings['disallow_sendBody']) ? '_body' : '') . (!empty($to_names) ? '_tolist' : '');
    foreach ($notifications as $lang => $notification_list) {
        // Using maillist functionality
        if ($maillist) {
            $sender_details = query_sender_wrapper($from['id']);
            $from_wrapper = !empty($modSettings['maillist_mail_from']) ? $modSettings['maillist_mail_from'] : (empty($modSettings['maillist_sitename_address']) ? $webmaster_email : $modSettings['maillist_sitename_address']);
            // Add in the signature
            $replacements['SIGNATURE'] = $sender_details['signature'];
            // And off it goes, looking a bit more personal
            $mail = loadEmailTemplate($email_template, $replacements, $lang);
            $reference = !empty($pm_head) ? $pm_head : null;
            sendmail($notification_list, $mail['subject'], $mail['body'], $from['name'], 'p' . $id_pm, false, 2, null, true, $from_wrapper, $reference);
        } else {
            // Off the notification email goes!
            $mail = loadEmailTemplate($email_template, $replacements, $lang);
            sendmail($notification_list, $mail['subject'], $mail['body'], null, 'p' . $id_pm, false, 2, null, true);
        }
    }
    // Integrated After PMs
    call_integration_hook('integrate_personal_message_after', array(&$id_pm, &$log, &$recipients, &$from, &$subject, &$message));
    // Back to what we were on before!
    loadLanguage('index+PersonalMessage');
    // Add one to their unread and read message counts.
    foreach ($all_to as $k => $id) {
        if (isset($deletes[$id])) {
            unset($all_to[$k]);
        }
    }
    if (!empty($all_to)) {
        updateMemberData($all_to, array('personal_messages' => '+', 'unread_messages' => '+', 'new_pm' => 1));
    }
    return $log;
}
示例#4
0
    /**
     * Send out a daily email of all subscribed topics, to members.
     *
     * - It sends notifications about replies or new topics,
     * and moderation actions.
     */
    public function daily_digest()
    {
        global $is_weekly, $txt, $mbname, $scripturl, $modSettings, $boardurl;
        $db = database();
        // We'll want this...
        require_once SUBSDIR . '/Mail.subs.php';
        loadEssentialThemeData();
        // If the maillist function is on then so is the enhanced digest
        $maillist = !empty($modSettings['maillist_enabled']) && !empty($modSettings['maillist_digest_enabled']);
        if ($maillist) {
            require_once SUBSDIR . '/Emailpost.subs.php';
        }
        $is_weekly = !empty($is_weekly) ? 1 : 0;
        // Right - get all the notification data FIRST.
        $request = $db->query('', '
			SELECT ln.id_topic, COALESCE(t.id_board, ln.id_board) AS id_board, mem.email_address, mem.member_name, mem.real_name, mem.notify_types,
				mem.lngfile, mem.id_member
			FROM {db_prefix}log_notify AS ln
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
				LEFT JOIN {db_prefix}topics AS t ON (ln.id_topic != {int:empty_topic} AND t.id_topic = ln.id_topic)
			WHERE mem.notify_regularity = {int:notify_regularity}
				AND mem.is_activated = {int:is_activated}', array('empty_topic' => 0, 'notify_regularity' => $is_weekly ? '3' : '2', 'is_activated' => 1));
        $members = array();
        $langs = array();
        $notify = array();
        $boards = array();
        while ($row = $db->fetch_assoc($request)) {
            if (!isset($members[$row['id_member']])) {
                $members[$row['id_member']] = array('email' => $row['email_address'], 'name' => $row['real_name'] == '' ? $row['member_name'] : un_htmlspecialchars($row['real_name']), 'id' => $row['id_member'], 'notifyMod' => $row['notify_types'] < 3 ? true : false, 'lang' => $row['lngfile']);
                $langs[$row['lngfile']] = $row['lngfile'];
            }
            // Store this useful data!
            $boards[$row['id_board']] = $row['id_board'];
            if ($row['id_topic']) {
                $notify['topics'][$row['id_topic']][] = $row['id_member'];
            } else {
                $notify['boards'][$row['id_board']][] = $row['id_member'];
            }
        }
        $db->free_result($request);
        if (empty($boards)) {
            return true;
        }
        // Just get the board names.
        require_once SUBSDIR . '/Boards.subs.php';
        $boards = fetchBoardsInfo(array('boards' => $boards), array('override_permissions' => true));
        if (empty($boards)) {
            return true;
        }
        // Get the actual topics...
        $request = $db->query('', '
			SELECT ld.note_type, t.id_topic, t.id_board, t.id_member_started, m.id_msg, m.subject, m.body, ld.id_msg AS last_reply,
				b.name AS board_name, ml.body as last_body
			FROM {db_prefix}log_digest AS ld
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ld.id_topic
					AND t.id_board IN ({array_int:board_list}))
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
				INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = ld.id_msg)
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
			WHERE ' . ($is_weekly ? 'ld.daily != {int:daily_value}' : 'ld.daily IN (0, 2)'), array('board_list' => array_keys($boards), 'daily_value' => 2));
        $types = array();
        while ($row = $db->fetch_assoc($request)) {
            if (!isset($types[$row['note_type']][$row['id_board']])) {
                $types[$row['note_type']][$row['id_board']] = array('lines' => array(), 'name' => un_htmlspecialchars($row['board_name']), 'id' => $row['id_board']);
            }
            // A reply has been made
            if ($row['note_type'] === 'reply') {
                // More than one reply to this topic?
                if (isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']])) {
                    $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['count']++;
                    // keep track of the highest numbered reply and body text for this topic ...
                    if ($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['body_id'] < $row['last_reply']) {
                        $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['body_id'] = $row['last_reply'];
                        $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['body_text'] = $row['last_body'];
                    }
                } else {
                    // First time we have seen a reply to this topic, so load our array
                    $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array('id' => $row['id_topic'], 'subject' => un_htmlspecialchars($row['subject']), 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new', 'count' => 1, 'body_id' => $row['last_reply'], 'body_text' => $row['last_body']);
                }
            } elseif ($row['note_type'] === 'topic') {
                if ($maillist) {
                    // Convert to markdown markup e.g. text ;)
                    pbe_prepare_text($row['body']);
                    $row['body'] = Util::shorten_text($row['body'], !empty($modSettings['digest_preview_length']) ? $modSettings['digest_preview_length'] : 375, true);
                    $row['body'] = preg_replace("~\n~s", "\n  ", $row['body']);
                }
                // Topics are simple since we are only concerned with the first post
                if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']])) {
                    $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array('id' => $row['id_topic'], 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new', 'subject' => un_htmlspecialchars($row['subject']), 'body' => $row['body']);
                }
            } elseif ($maillist && empty($modSettings['pbe_no_mod_notices'])) {
                if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']])) {
                    $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array('id' => $row['id_topic'], 'subject' => un_htmlspecialchars($row['subject']), 'starter' => $row['id_member_started']);
                }
            }
            $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array();
            if (!empty($notify['topics'][$row['id_topic']])) {
                $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array_merge($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'], $notify['topics'][$row['id_topic']]);
            }
            if (!empty($notify['boards'][$row['id_board']])) {
                $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array_merge($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'], $notify['boards'][$row['id_board']]);
            }
        }
        $db->free_result($request);
        if (empty($types)) {
            return true;
        }
        // Fix the last reply message so its suitable for previewing
        if ($maillist) {
            foreach ($types['reply'] as $id => $board) {
                foreach ($board['lines'] as $topic) {
                    // Replace the body array with the appropriate preview message
                    $body = $types['reply'][$id]['lines'][$topic['id']]['body_text'];
                    pbe_prepare_text($body);
                    $body = Util::shorten_text($body, !empty($modSettings['digest_preview_length']) ? $modSettings['digest_preview_length'] : 375, true);
                    $body = preg_replace("~\n~s", "\n  ", $body);
                    $types['reply'][$id]['lines'][$topic['id']]['body'] = $body;
                    unset($types['reply'][$id]['lines'][$topic['id']]['body_text'], $body);
                }
            }
        }
        // Let's load all the languages into a cache thingy.
        $langtxt = array();
        foreach ($langs as $lang) {
            loadLanguage('Post', $lang);
            loadLanguage('index', $lang);
            loadLanguage('Maillist', $lang);
            loadLanguage('EmailTemplates', $lang);
            $langtxt[$lang] = array('subject' => $txt['digest_subject_' . ($is_weekly ? 'weekly' : 'daily')], 'char_set' => 'UTF-8', 'intro' => sprintf($txt['digest_intro_' . ($is_weekly ? 'weekly' : 'daily')], $mbname), 'new_topics' => $txt['digest_new_topics'], 'topic_lines' => $txt['digest_new_topics_line'], 'new_replies' => $txt['digest_new_replies'], 'mod_actions' => $txt['digest_mod_actions'], 'replies_one' => $txt['digest_new_replies_one'], 'replies_many' => $txt['digest_new_replies_many'], 'sticky' => $txt['digest_mod_act_sticky'], 'lock' => $txt['digest_mod_act_lock'], 'unlock' => $txt['digest_mod_act_unlock'], 'remove' => $txt['digest_mod_act_remove'], 'move' => $txt['digest_mod_act_move'], 'merge' => $txt['digest_mod_act_merge'], 'split' => $txt['digest_mod_act_split'], 'bye' => (!empty($modSettings['maillist_sitename_regards']) ? $modSettings['maillist_sitename_regards'] : '') . "\n" . $boardurl, 'preview' => $txt['digest_preview'], 'see_full' => $txt['digest_see_full'], 'reply_preview' => $txt['digest_reply_preview'], 'unread_reply_link' => $txt['digest_unread_reply_link']);
        }
        // Right - send out the silly things - this will take quite some space!
        foreach ($members as $mid => $member) {
            // Do the start stuff!
            $email = array('subject' => $mbname . ' - ' . $langtxt[$lang]['subject'], 'body' => $member['name'] . ',' . "\n\n" . $langtxt[$lang]['intro'] . "\n" . $scripturl . '?action=profile;area=notification;u=' . $member['id'] . "\n", 'email' => $member['email']);
            // All the new topics
            if (isset($types['topic'])) {
                $titled = false;
                // Each type contains a board ID and then a topic number
                foreach ($types['topic'] as $id => $board) {
                    foreach ($board['lines'] as $topic) {
                        // They have requested notification for new topics in this board
                        if (in_array($mid, $topic['members'])) {
                            // Start of the new topics with a heading bar
                            if (!$titled) {
                                $email['body'] .= "\n" . $langtxt[$lang]['new_topics'] . ':' . "\n" . str_repeat('-', 78);
                                $titled = true;
                            }
                            $email['body'] .= "\n" . sprintf($langtxt[$lang]['topic_lines'], $topic['subject'], $board['name']);
                            if ($maillist) {
                                $email['body'] .= $langtxt[$lang]['preview'] . $topic['body'] . $langtxt[$lang]['see_full'] . $topic['link'] . "\n";
                            }
                        }
                    }
                }
                if ($titled) {
                    $email['body'] .= "\n";
                }
            }
            // What about replies?
            if (isset($types['reply'])) {
                $titled = false;
                // Each reply will have a board id and then a topic ID
                foreach ($types['reply'] as $id => $board) {
                    foreach ($board['lines'] as $topic) {
                        // This member wants notices on replys to this topic
                        if (in_array($mid, $topic['members'])) {
                            // First one in the section gets a nice heading
                            if (!$titled) {
                                $email['body'] .= "\n" . $langtxt[$lang]['new_replies'] . ':' . "\n" . str_repeat('-', 78);
                                $titled = true;
                            }
                            $email['body'] .= "\n" . ($topic['count'] === 1 ? sprintf($langtxt[$lang]['replies_one'], $topic['subject']) : sprintf($langtxt[$lang]['replies_many'], $topic['count'], $topic['subject']));
                            if ($maillist) {
                                $email['body'] .= $langtxt[$lang]['reply_preview'] . $topic['body'] . $langtxt[$lang]['unread_reply_link'] . $topic['link'] . "\n";
                            }
                        }
                    }
                }
                if ($titled) {
                    $email['body'] .= "\n";
                }
            }
            // Finally, moderation actions!
            $titled = false;
            foreach ($types as $note_type => $type) {
                if ($note_type === 'topic' || $note_type === 'reply') {
                    continue;
                }
                foreach ($type as $id => $board) {
                    foreach ($board['lines'] as $topic) {
                        if (in_array($mid, $topic['members'])) {
                            if (!$titled) {
                                $email['body'] .= "\n" . $langtxt[$lang]['mod_actions'] . ':' . "\n" . str_repeat('-', 47);
                                $titled = true;
                            }
                            $email['body'] .= "\n" . sprintf($langtxt[$lang][$note_type], $topic['subject']);
                        }
                    }
                }
            }
            if ($titled) {
                $email['body'] .= "\n";
            }
            // Then just say our goodbyes!
            $email['body'] .= "\n\n" . $langtxt[$lang]['bye'];
            // Send it - low priority!
            sendmail($email['email'], $email['subject'], $email['body'], null, null, false, 4);
        }
        // Using the queue, do a final flush before we say thats all folks
        if (!empty($modSettings['mail_queue'])) {
            AddMailQueue(true);
        }
        // Clean up...
        if ($is_weekly) {
            $db->query('', '
				DELETE FROM {db_prefix}log_digest
				WHERE daily != {int:not_daily}', array('not_daily' => 0));
            $db->query('', '
				UPDATE {db_prefix}log_digest
				SET daily = {int:daily_value}
				WHERE daily = {int:not_daily}', array('daily_value' => 2, 'not_daily' => 0));
        } else {
            // Clear any only weekly ones, and stop us from sending daily again.
            $db->query('', '
				DELETE FROM {db_prefix}log_digest
				WHERE daily = {int:daily_value}', array('daily_value' => 2));
            $db->query('', '
				UPDATE {db_prefix}log_digest
				SET daily = {int:both_value}
				WHERE daily = {int:no_value}', array('both_value' => 1, 'no_value' => 0));
        }
        // Just in case the member changes their settings mark this as sent.
        $members = array_keys($members);
        $db->query('', '
			UPDATE {db_prefix}log_notify
			SET sent = {int:is_sent}
			WHERE id_member IN ({array_int:member_list})', array('member_list' => $members, 'is_sent' => 1));
        // Log we've done it...
        return true;
    }