public static function email_digest()
 {
     global $CFG, $PERF;
     // Build current digest.
     mtrace("Beginning forum digest processing...");
     if (!empty($PERF->dbqueries)) {
         $beforequeries = $PERF->dbqueries;
     }
     $before = microtime(true);
     mtrace('Initial query: ', '');
     $list = new mod_forumng_digest_list(true);
     mtrace(round(microtime(true) - $before, 1) . 's');
     $userdigests = array();
     $oldcourse = null;
     // Forum loop.
     while ($list->next_forum($forum, $cm, $context, $course)) {
         self::debug("DEBUG: Forum " . $forum->get_name() . " on course {$course->shortname} " . "(cmid {$cm->id} contextid {$context->id})");
         if (!$oldcourse || $course->id != $oldcourse->id) {
             // Finish off and clear users.
             if ($oldcourse) {
                 self::digest_finish_course($oldcourse, $userdigests);
             }
             // Set up new course details.
             // Note: This code is a bit sketchy; borrowed from cron_setup_user.
             $PAGE = new moodle_page();
             $PAGE->set_course($course);
             $oldcourse = clone $course;
         }
         // Count posts just for logging.
         $postcount = 0;
         // Get subscribers to forum.
         $subscribers = $forum->get_subscribers();
         self::debug("DEBUG: Subscribers before filter " . count($subscribers), '');
         self::email_filter_subscribers($course, $cm, $forum, $subscribers, true);
         self::debug(", after " . count($subscribers));
         if (count($subscribers) == 0) {
             continue;
         }
         while ($list->next_discussion($discussion)) {
             self::debug("DEBUG: Discussion " . $discussion->get_subject() . ' (' . $discussion->get_id() . ')');
             // Evaluate list of users based on this discussion (which holds
             // group info). Organise list by language, timezone and email
             // type.
             $discussionusers = array();
             foreach ($subscribers as $subscriber) {
                 if (self::subscriber_receives_discussion($forum, $discussion, $subscriber)) {
                     $discussionusers[$subscriber->id] = $subscriber;
                 }
             }
             while ($list->next_post($post, $inreplyto)) {
                 // Loop through all digest users.
                 foreach ($discussionusers as $user) {
                     // Add to digest. (This will set up the user's
                     // digest if they don't already have one).
                     self::digest_add_post_for_user($user, $userdigests, $post, $inreplyto, $discussion, $forum, $cm, $course, $context);
                 }
                 $postcount++;
             }
         }
     }
     if ($oldcourse) {
         self::digest_finish_course($oldcourse, $userdigests);
     }
     $queryinfo = '';
     if (!empty($PERF->dbqueries)) {
         $queryinfo = ', ' . ($PERF->dbqueries - $beforequeries) . ' queries';
     }
     $totalpostcount = $list->get_post_count_so_far();
     mtrace("Digest processing ({$totalpostcount} new digest posts) complete, total: " . round(microtime(true) - $before, 1) . 's' . $queryinfo);
 }
 static function email_digest()
 {
     global $CFG, $PERF;
     // Do digest mails if required. Note this is based on server time not
     // user time.
     $nextdigest = get_config('forumng', 'nextdigest');
     if (!$nextdigest) {
         // Run digest at next occurrence of the requested time
         $nextdigest = strtotime($CFG->digestmailtime . ':00');
         if ($nextdigest <= time()) {
             $nextdigest = strtotime('+1 day', $nextdigest);
         }
         set_config('nextdigest', $nextdigest, 'forumng');
     }
     if (time() < $nextdigest) {
         self::debug("DEBUG: Not yet time for digest");
         return;
     }
     // Run digest again next day at specified time (note: best to
     // get time again, as they may have changed it)
     $nextdigest = strtotime($CFG->digestmailtime . ':00');
     if ($nextdigest <= time()) {
         $nextdigest = strtotime('+1 day', $nextdigest);
     }
     set_config('nextdigest', $nextdigest, 'forumng');
     // OK, now build current digest
     mtrace("Beginning forum digest processing...");
     if (!empty($PERF->dbqueries)) {
         $beforequeries = $PERF->dbqueries;
     }
     $before = microtime(true);
     mtrace('Initial query: ', '');
     $list = new mod_forumng_digest_list(true);
     mtrace(round(microtime(true) - $before, 1) . 's');
     $userdigests = array();
     $oldcourse = null;
     // Forum loop
     while ($list->next_forum($forum, $cm, $context, $course)) {
         self::debug("DEBUG: Forum " . $forum->get_name() . " on course {$course->shortname} " . "(cmid {$cm->id} contextid {$context->id})");
         if (!$oldcourse || $course->id != $oldcourse->id) {
             // Finish off and clear users
             if ($oldcourse) {
                 self::digest_finish_course($oldcourse, $userdigests);
             }
             // Set up new course details
             // Note: This code is a bit sketchy; borrowed from cron_setup_user
             $PAGE = new moodle_page();
             $PAGE->set_course($course);
             $oldcourse = clone $course;
         }
         // Count posts just for logging
         $postcount = 0;
         // Get subscribers to forum
         $subscribers = $forum->get_subscribers();
         self::debug("DEBUG: Subscribers before filter " . count($subscribers), '');
         self::email_filter_subscribers($course, $cm, $forum, $subscribers, true);
         self::debug(", after " . count($subscribers));
         if (count($subscribers) == 0) {
             continue;
         }
         while ($list->next_discussion($discussion)) {
             self::debug("DEBUG: Discussion " . $discussion->get_subject() . ' (' . $discussion->get_id() . ')');
             // Evaluate list of users based on this discussion (which holds
             // group info). Organise list by language, timezone and email
             // type.
             $discussionusers = array();
             foreach ($subscribers as $subscriber) {
                 if (self::subscriber_receives_discussion($forum, $discussion, $subscriber)) {
                     $discussionusers[$subscriber->id] = $subscriber;
                 }
             }
             while ($list->next_post($post, $inreplyto)) {
                 // Loop through all digest users
                 foreach ($discussionusers as $user) {
                     // Add to digest. (This will set up the user's
                     // digest if they don't already have one)
                     self::digest_add_post_for_user($user, $userdigests, $post, $inreplyto, $discussion, $forum, $cm, $course, $context);
                 }
                 $postcount++;
             }
         }
     }
     if ($oldcourse) {
         self::digest_finish_course($oldcourse, $userdigests);
     }
     $queryinfo = '';
     if (!empty($PERF->dbqueries)) {
         $queryinfo = ', ' . ($PERF->dbqueries - $beforequeries) . ' queries';
     }
     $totalpostcount = $list->get_post_count_so_far();
     mtrace("Digest processing ({$totalpostcount} new digest posts) complete, total: " . round(microtime(true) - $before, 1) . 's' . $queryinfo);
 }