/** * Test that unusual combinations of discussion subscriptions do not affect the subscribed user list. */ public function test_fetch_subscribed_users_discussion_subscriptions() { global $DB; $this->resetAfterTest(true); // Create a course, with a twf. where users are initially subscribed. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); $twf = $this->getDataGenerator()->create_module('twf', $options); // Create some user enrolled in the course as a student. $usercount = 5; $users = $this->helper_create_users($course, $usercount); list($discussion, $post) = $this->helper_post_to_twf($twf, $users[0]); // All users should be subscribed. $subscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf); $this->assertEquals($usercount, count($subscribers)); $subscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf, 0, null, null, true); $this->assertEquals($usercount, count($subscribers)); \mod_twf\subscriptions::unsubscribe_user_from_discussion($users[0]->id, $discussion); // All users should be subscribed. $subscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf); $this->assertEquals($usercount, count($subscribers)); // All users should be subscribed. $subscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf, 0, null, null, true); $this->assertEquals($usercount, count($subscribers)); // Manually insert an extra subscription for one of the users. $record = new stdClass(); $record->userid = $users[2]->id; $record->twf = $twf->id; $record->discussion = $discussion->id; $record->preference = time(); $DB->insert_record('twf_discussion_subs', $record); // The discussion count should not have changed. $subscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf); $this->assertEquals($usercount, count($subscribers)); $subscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf, 0, null, null, true); $this->assertEquals($usercount, count($subscribers)); // Unsubscribe 2 users. $unsubscribedcount = 2; for ($i = 0; $i < $unsubscribedcount; $i++) { \mod_twf\subscriptions::unsubscribe_user($users[$i]->id, $twf); } // The subscription count should now take into account those users who have been unsubscribed. $subscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf); $this->assertEquals($usercount - $unsubscribedcount, count($subscribers)); $subscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf, 0, null, null, true); $this->assertEquals($usercount - $unsubscribedcount, count($subscribers)); // Now subscribe one of those users back to the discussion. $subscribeddiscussionusers = 1; for ($i = 0; $i < $subscribeddiscussionusers; $i++) { \mod_twf\subscriptions::subscribe_user_to_discussion($users[$i]->id, $discussion); } $subscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf); $this->assertEquals($usercount - $unsubscribedcount, count($subscribers)); $subscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf, 0, null, null, true); $this->assertEquals($usercount - $unsubscribedcount + $subscribeddiscussionusers, count($subscribers)); }
$twfs = $modinfo->get_instances_of('twf'); if (!array_key_exists($twfto->id, $twfs)) { print_error('cannotmovetonotfound', 'twf', $return); } $cmto = $twfs[$twfto->id]; if (!$cmto->uservisible) { print_error('cannotmovenotvisible', 'twf', $return); } $destinationctx = context_module::instance($cmto->id); require_capability('mod/twf:startdiscussion', $destinationctx); if (!twf_move_attachments($discussion, $twf->id, $twfto->id)) { echo $OUTPUT->notification("Errors occurred while moving attachment directories - check your file permissions"); } // For each subscribed user in this twf and discussion, copy over per-discussion subscriptions if required. $discussiongroup = $discussion->groupid == -1 ? 0 : $discussion->groupid; $potentialsubscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf, $discussiongroup, $modcontext, 'u.id', true); // Pre-seed the subscribed_discussion caches. // Firstly for the twf being moved to. \mod_twf\subscriptions::fill_subscription_cache($twfto->id); // And also for the discussion being moved. \mod_twf\subscriptions::fill_subscription_cache($twf->id); $subscriptionchanges = array(); $subscriptiontime = time(); foreach ($potentialsubscribers as $subuser) { $userid = $subuser->id; $targetsubscription = \mod_twf\subscriptions::is_subscribed($userid, $twfto, null, $cmto); $discussionsubscribed = \mod_twf\subscriptions::is_subscribed($userid, $twf, $discussion->id); $twfsubscribed = \mod_twf\subscriptions::is_subscribed($userid, $twf); if ($twfsubscribed && !$discussionsubscribed && $targetsubscription) { // The user has opted out of this discussion and the move would cause them to receive notifications again. // Ensure they are unsubscribed from the discussion still.
/** * Returns list of user objects that are subscribed to this twf. * * @param stdClass $course the course * @param stdClass $twf the twf * @param int $groupid group id, or 0 for all. * @param context_module $context the twf context, to save re-fetching it where possible. * @param string $fields requested user fields (with "u." table prefix) * @param boolean $considerdiscussions Whether to take discussion subscriptions and unsubscriptions into consideration. * @return array list of users. * @deprecated since Moodle 2.8 use \mod_twf\subscriptions::fetch_subscribed_users() instead */ function twf_subscribed_users($course, $twf, $groupid = 0, $context = null, $fields = null) { debugging("twf_subscribed_users() has been deprecated, please use \\mod_twf\\subscriptions::fetch_subscribed_users() instead.", DEBUG_DEVELOPER); \mod_twf\subscriptions::fetch_subscribed_users($twf, $groupid, $context, $fields); }
$strsubscribers = get_string("subscribers", "twf"); $PAGE->navbar->add($strsubscribers); $PAGE->set_title($strsubscribers); $PAGE->set_heading($COURSE->fullname); if (has_capability('mod/twf:managesubscriptions', $context) && \mod_twf\subscriptions::is_forcesubscribed($twf) === false) { if ($edit != -1) { $USER->subscriptionsediting = $edit; } $PAGE->set_button(twf_update_subscriptions_button($course->id, $id)); } else { unset($USER->subscriptionsediting); } echo $OUTPUT->header(); echo $OUTPUT->heading(get_string('twf', 'twf') . ' ' . $strsubscribers); if (empty($USER->subscriptionsediting)) { $subscribers = \mod_twf\subscriptions::fetch_subscribed_users($twf, $currentgroup, $context); if (\mod_twf\subscriptions::is_forcesubscribed($twf)) { $subscribers = mod_twf_filter_hidden_users($cm, $context, $subscribers); } echo $twfoutput->subscriber_overview($subscribers, $twf, $course); } else { echo $twfoutput->subscriber_selection_form($existingselector, $subscriberselector); } echo $OUTPUT->footer(); /** * Filters a list of users for whether they can see a given activity. * If the course module is hidden (closed-eye icon), then only users who have * the permission to view hidden activities will appear in the output list. * * @todo MDL-48625 This filtering should be handled in core libraries instead. *
/** * Test subscription using disallow subscription on create. */ public function test_twf_disallow_subscribe_on_create() { global $CFG; $this->resetAfterTest(); $usercount = 5; $course = $this->getDataGenerator()->create_course(); $users = array(); for ($i = 0; $i < $usercount; $i++) { $user = $this->getDataGenerator()->create_user(); $users[] = $user; $this->getDataGenerator()->enrol_user($user->id, $course->id); } $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE); // Subscription prevented. $twf = $this->getDataGenerator()->create_module('twf', $options); $result = \mod_twf\subscriptions::fetch_subscribed_users($twf); // No subscriptions by default. $this->assertEquals(0, count($result)); foreach ($users as $user) { $this->assertFalse(\mod_twf\subscriptions::is_subscribed($user->id, $twf)); } }
/** * Function to be run periodically according to the scheduled task. * * Finds all posts that have yet to be mailed out, and mails them * out to all subscribers as well as other maintance tasks. * * NOTE: Since 2.7.2 this function is run by scheduled task rather * than standard cron. * * @todo MDL-44734 The function will be split up into seperate tasks. */ function twf_cron() { global $CFG, $USER, $DB; $site = get_site(); // All users that are subscribed to any post that needs sending, // please increase $CFG->extramemorylimit on large sites that // send notifications to a large number of users. $users = array(); $userscount = 0; // Cached user counter - count($users) in PHP is horribly slow!!! // Status arrays. $mailcount = array(); $errorcount = array(); // caches $discussions = array(); $twfs = array(); $courses = array(); $coursemodules = array(); $subscribedusers = array(); $messageinboundhandlers = array(); // Posts older than 2 days will not be mailed. This is to avoid the problem where // cron has not been running for a long time, and then suddenly people are flooded // with mail from the past few weeks or months $timenow = time(); $endtime = $timenow - $CFG->maxeditingtime; $starttime = $endtime - 48 * 3600; // Two days earlier // Get the list of twf subscriptions for per-user per-twf maildigest settings. $digestsset = $DB->get_recordset('twf_digests', null, '', 'id, userid, twf, maildigest'); $digests = array(); foreach ($digestsset as $thisrow) { if (!isset($digests[$thisrow->twf])) { $digests[$thisrow->twf] = array(); } $digests[$thisrow->twf][$thisrow->userid] = $thisrow->maildigest; } $digestsset->close(); // Create the generic messageinboundgenerator. $messageinboundgenerator = new \core\message\inbound\address_manager(); $messageinboundgenerator->set_handler('\\mod_twf\\message\\inbound\\reply_handler'); if ($posts = twf_get_unmailed_posts($starttime, $endtime, $timenow)) { // Mark them all now as being mailed. It's unlikely but possible there // might be an error later so that a post is NOT actually mailed out, // but since mail isn't crucial, we can accept this risk. Doing it now // prevents the risk of duplicated mails, which is a worse problem. if (!twf_mark_old_posts_as_mailed($endtime)) { mtrace('Errors occurred while trying to mark some posts as being mailed.'); return false; // Don't continue trying to mail them, in case we are in a cron loop } // checking post validity, and adding users to loop through later foreach ($posts as $pid => $post) { $discussionid = $post->discussion; if (!isset($discussions[$discussionid])) { if ($discussion = $DB->get_record('twf_discussions', array('id' => $post->discussion))) { $discussions[$discussionid] = $discussion; \mod_twf\subscriptions::fill_subscription_cache($discussion->twf); \mod_twf\subscriptions::fill_discussion_subscription_cache($discussion->twf); } else { mtrace('Could not find discussion ' . $discussionid); unset($posts[$pid]); continue; } } $twfid = $discussions[$discussionid]->twf; if (!isset($twfs[$twfid])) { if ($twf = $DB->get_record('twf', array('id' => $twfid))) { $twfs[$twfid] = $twf; } else { mtrace('Could not find twf ' . $twfid); unset($posts[$pid]); continue; } } $courseid = $twfs[$twfid]->course; if (!isset($courses[$courseid])) { if ($course = $DB->get_record('course', array('id' => $courseid))) { $courses[$courseid] = $course; } else { mtrace('Could not find course ' . $courseid); unset($posts[$pid]); continue; } } if (!isset($coursemodules[$twfid])) { if ($cm = get_coursemodule_from_instance('twf', $twfid, $courseid)) { $coursemodules[$twfid] = $cm; } else { mtrace('Could not find course module for twf ' . $twfid); unset($posts[$pid]); continue; } } // Save the Inbound Message datakey here to reduce DB queries later. $messageinboundgenerator->set_data($pid); $messageinboundhandlers[$pid] = $messageinboundgenerator->fetch_data_key(); // Caching subscribed users of each twf. if (!isset($subscribedusers[$twfid])) { $modcontext = context_module::instance($coursemodules[$twfid]->id); if ($subusers = \mod_twf\subscriptions::fetch_subscribed_users($twfs[$twfid], 0, $modcontext, 'u.*', true)) { foreach ($subusers as $postuser) { // this user is subscribed to this twf $subscribedusers[$twfid][$postuser->id] = $postuser->id; $userscount++; if ($userscount > FORUM_CRON_USER_CACHE) { // Store minimal user info. $minuser = new stdClass(); $minuser->id = $postuser->id; $users[$postuser->id] = $minuser; } else { // Cache full user record. twf_cron_minimise_user_record($postuser); $users[$postuser->id] = $postuser; } } // Release memory. unset($subusers); unset($postuser); } } $mailcount[$pid] = 0; $errorcount[$pid] = 0; } } if ($users && $posts) { $urlinfo = parse_url($CFG->wwwroot); $hostname = $urlinfo['host']; foreach ($users as $userto) { // Terminate if processing of any account takes longer than 2 minutes. core_php_time_limit::raise(120); mtrace('Processing user ' . $userto->id); // Init user caches - we keep the cache for one cycle only, otherwise it could consume too much memory. if (isset($userto->username)) { $userto = clone $userto; } else { $userto = $DB->get_record('user', array('id' => $userto->id)); twf_cron_minimise_user_record($userto); } $userto->viewfullnames = array(); $userto->canpost = array(); $userto->markposts = array(); // Setup this user so that the capabilities are cached, and environment matches receiving user. cron_setup_user($userto); // Reset the caches. foreach ($coursemodules as $twfid => $unused) { $coursemodules[$twfid]->cache = new stdClass(); $coursemodules[$twfid]->cache->caps = array(); unset($coursemodules[$twfid]->uservisible); } foreach ($posts as $pid => $post) { $discussion = $discussions[$post->discussion]; $twf = $twfs[$discussion->twf]; $course = $courses[$twf->course]; $cm =& $coursemodules[$twf->id]; // Do some checks to see if we can bail out now. // Only active enrolled users are in the list of subscribers. // This does not necessarily mean that the user is subscribed to the twf or to the discussion though. if (!isset($subscribedusers[$twf->id][$userto->id])) { // The user does not subscribe to this twf. continue; } if (!\mod_twf\subscriptions::is_subscribed($userto->id, $twf, $post->discussion, $coursemodules[$twf->id])) { // The user does not subscribe to this twf, or to this specific discussion. continue; } if ($subscriptiontime = \mod_twf\subscriptions::fetch_discussion_subscription($twf->id, $userto->id)) { // Skip posts if the user subscribed to the discussion after it was created. if (isset($subscriptiontime[$post->discussion]) && $subscriptiontime[$post->discussion] > $post->created) { continue; } } // Don't send email if the twf is Q&A and the user has not posted. // Initial topics are still mailed. if ($twf->type == 'qanda' && !twf_get_user_posted_time($discussion->id, $userto->id) && $pid != $discussion->firstpost) { mtrace('Did not email ' . $userto->id . ' because user has not posted in discussion'); continue; } // Get info about the sending user. if (array_key_exists($post->userid, $users)) { // We might know the user already. $userfrom = $users[$post->userid]; if (!isset($userfrom->idnumber)) { // Minimalised user info, fetch full record. $userfrom = $DB->get_record('user', array('id' => $userfrom->id)); twf_cron_minimise_user_record($userfrom); } } else { if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) { twf_cron_minimise_user_record($userfrom); // Fetch only once if possible, we can add it to user list, it will be skipped anyway. if ($userscount <= FORUM_CRON_USER_CACHE) { $userscount++; $users[$userfrom->id] = $userfrom; } } else { mtrace('Could not find user ' . $post->userid . ', author of post ' . $post->id . '. Unable to send message.'); continue; } } // Note: If we want to check that userto and userfrom are not the same person this is probably the spot to do it. // Setup global $COURSE properly - needed for roles and languages. cron_setup_user($userto, $course); // Fill caches. if (!isset($userto->viewfullnames[$twf->id])) { $modcontext = context_module::instance($cm->id); $userto->viewfullnames[$twf->id] = has_capability('moodle/site:viewfullnames', $modcontext); } if (!isset($userto->canpost[$discussion->id])) { $modcontext = context_module::instance($cm->id); $userto->canpost[$discussion->id] = twf_user_can_post($twf, $discussion, $userto, $cm, $course, $modcontext); } if (!isset($userfrom->groups[$twf->id])) { if (!isset($userfrom->groups)) { $userfrom->groups = array(); if (isset($users[$userfrom->id])) { $users[$userfrom->id]->groups = array(); } } $userfrom->groups[$twf->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid); if (isset($users[$userfrom->id])) { $users[$userfrom->id]->groups[$twf->id] = $userfrom->groups[$twf->id]; } } // Make sure groups allow this user to see this email. if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used. if (!groups_group_exists($discussion->groupid)) { // Can't find group - be safe and don't this message. continue; } if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $modcontext)) { // Do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS. continue; } } // Make sure we're allowed to see the post. if (!twf_user_can_see_post($twf, $discussion, $post, null, $cm)) { mtrace('User ' . $userto->id . ' can not see ' . $post->id . '. Not sending message.'); continue; } // OK so we need to send the email. // Does the user want this post in a digest? If so postpone it for now. $maildigest = twf_get_user_maildigest_bulk($digests, $userto, $twf->id); if ($maildigest > 0) { // This user wants the mails to be in digest form. $queue = new stdClass(); $queue->userid = $userto->id; $queue->discussionid = $discussion->id; $queue->postid = $post->id; $queue->timemodified = $post->created; $DB->insert_record('twf_queue', $queue); continue; } // Prepare to actually send the post now, and build up the content. $cleantwfname = str_replace('"', "'", strip_tags(format_string($twf->name))); $userfrom->customheaders = array('List-Id: "' . $cleantwfname . '" <moodletwf' . $twf->id . '@' . $hostname . '>', 'List-Help: ' . $CFG->wwwroot . '/mod/twf/view.php?f=' . $twf->id, 'Message-ID: ' . twf_get_email_message_id($post->id, $userto->id, $hostname), 'X-Course-Id: ' . $course->id, 'X-Course-Name: ' . format_string($course->fullname, true), 'Precedence: Bulk', 'X-Auto-Response-Suppress: All', 'Auto-Submitted: auto-generated'); if ($post->parent) { // This post is a reply, so add headers for threading (see MDL-22551). $userfrom->customheaders[] = 'In-Reply-To: ' . twf_get_email_message_id($post->parent, $userto->id, $hostname); $userfrom->customheaders[] = 'References: ' . twf_get_email_message_id($post->parent, $userto->id, $hostname); } $shortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id))); // Generate a reply-to address from using the Inbound Message handler. $replyaddress = null; if ($userto->canpost[$discussion->id] && array_key_exists($post->id, $messageinboundhandlers)) { $messageinboundgenerator->set_data($post->id, $messageinboundhandlers[$post->id]); $replyaddress = $messageinboundgenerator->generate($userto->id); } $a = new stdClass(); $a->courseshortname = $shortname; $a->twfname = $cleantwfname; $a->subject = format_string($post->subject, true); $postsubject = html_to_text(get_string('postmailsubject', 'twf', $a), 0); $posttext = twf_make_mail_text($course, $cm, $twf, $discussion, $post, $userfrom, $userto, false, $replyaddress); $posthtml = twf_make_mail_html($course, $cm, $twf, $discussion, $post, $userfrom, $userto, $replyaddress); // Send the post now! mtrace('Sending ', ''); $eventdata = new \core\message\message(); $eventdata->component = 'mod_twf'; $eventdata->name = 'posts'; $eventdata->userfrom = $userfrom; $eventdata->userto = $userto; $eventdata->subject = $postsubject; $eventdata->fullmessage = $posttext; $eventdata->fullmessageformat = FORMAT_PLAIN; $eventdata->fullmessagehtml = $posthtml; $eventdata->notification = 1; $eventdata->replyto = $replyaddress; if (!empty($replyaddress)) { // Add extra text to email messages if they can reply back. $textfooter = "\n\n" . get_string('replytopostbyemail', 'mod_twf'); $htmlfooter = html_writer::tag('p', get_string('replytopostbyemail', 'mod_twf')); $additionalcontent = array('fullmessage' => array('footer' => $textfooter), 'fullmessagehtml' => array('footer' => $htmlfooter)); $eventdata->set_additional_content('email', $additionalcontent); } // If twf_replytouser is not set then send mail using the noreplyaddress. if (empty($CFG->twf_replytouser)) { $eventdata->userfrom = core_user::get_noreply_user(); } $smallmessagestrings = new stdClass(); $smallmessagestrings->user = fullname($userfrom); $smallmessagestrings->twfname = "{$shortname}: " . format_string($twf->name, true) . ": " . $discussion->name; $smallmessagestrings->message = $post->message; // Make sure strings are in message recipients language. $eventdata->smallmessage = get_string_manager()->get_string('smallmessage', 'twf', $smallmessagestrings, $userto->lang); $contexturl = new moodle_url('/mod/twf/discuss.php', array('d' => $discussion->id), 'p' . $post->id); $eventdata->contexturl = $contexturl->out(); $eventdata->contexturlname = $discussion->name; $mailresult = message_send($eventdata); if (!$mailresult) { mtrace("Error: mod/twf/lib.php twf_cron(): Could not send out mail for id {$post->id} to user {$userto->id}" . " ({$userto->email}) .. not trying again."); $errorcount[$post->id]++; } else { $mailcount[$post->id]++; // Mark post as read if twf_usermarksread is set off. if (!$CFG->twf_usermarksread) { $userto->markposts[$post->id] = $post->id; } } mtrace('post ' . $post->id . ': ' . $post->subject); } // Mark processed posts as read. twf_tp_mark_posts_read($userto, $userto->markposts); unset($userto); } } if ($posts) { foreach ($posts as $post) { mtrace($mailcount[$post->id] . " users were sent post {$post->id}, '{$post->subject}'"); if ($errorcount[$post->id]) { $DB->set_field('twf_posts', 'mailed', FORUM_MAILED_ERROR, array('id' => $post->id)); } } } // release some memory unset($subscribedusers); unset($mailcount); unset($errorcount); cron_setup_user(); $sitetimezone = core_date::get_server_timezone(); // Now see if there are any digest mails waiting to be sent, and if we should send them mtrace('Starting digest processing...'); core_php_time_limit::raise(300); // terminate if not able to fetch all digests in 5 minutes if (!isset($CFG->digestmailtimelast)) { // To catch the first time set_config('digestmailtimelast', 0); } $timenow = time(); $digesttime = usergetmidnight($timenow, $sitetimezone) + $CFG->digestmailtime * 3600; // Delete any really old ones (normally there shouldn't be any) $weekago = $timenow - 7 * 24 * 3600; $DB->delete_records_select('twf_queue', "timemodified < ?", array($weekago)); mtrace('Cleaned old digest records'); if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) { mtrace('Sending twf digests: ' . userdate($timenow, '', $sitetimezone)); $digestposts_rs = $DB->get_recordset_select('twf_queue', "timemodified < ?", array($digesttime)); if ($digestposts_rs->valid()) { // We have work to do $usermailcount = 0; //caches - reuse the those filled before too $discussionposts = array(); $userdiscussions = array(); foreach ($digestposts_rs as $digestpost) { if (!isset($posts[$digestpost->postid])) { if ($post = $DB->get_record('twf_posts', array('id' => $digestpost->postid))) { $posts[$digestpost->postid] = $post; } else { continue; } } $discussionid = $digestpost->discussionid; if (!isset($discussions[$discussionid])) { if ($discussion = $DB->get_record('twf_discussions', array('id' => $discussionid))) { $discussions[$discussionid] = $discussion; } else { continue; } } $twfid = $discussions[$discussionid]->twf; if (!isset($twfs[$twfid])) { if ($twf = $DB->get_record('twf', array('id' => $twfid))) { $twfs[$twfid] = $twf; } else { continue; } } $courseid = $twfs[$twfid]->course; if (!isset($courses[$courseid])) { if ($course = $DB->get_record('course', array('id' => $courseid))) { $courses[$courseid] = $course; } else { continue; } } if (!isset($coursemodules[$twfid])) { if ($cm = get_coursemodule_from_instance('twf', $twfid, $courseid)) { $coursemodules[$twfid] = $cm; } else { continue; } } $userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid; $discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid; } $digestposts_rs->close(); /// Finished iteration, let's close the resultset // Data collected, start sending out emails to each user foreach ($userdiscussions as $userid => $thesediscussions) { core_php_time_limit::raise(120); // terminate if processing of any account takes longer than 2 minutes cron_setup_user(); mtrace(get_string('processingdigest', 'twf', $userid), '... '); // First of all delete all the queue entries for this user $DB->delete_records_select('twf_queue', "userid = ? AND timemodified < ?", array($userid, $digesttime)); // Init user caches - we keep the cache for one cycle only, // otherwise it would unnecessarily consume memory. if (array_key_exists($userid, $users) and isset($users[$userid]->username)) { $userto = clone $users[$userid]; } else { $userto = $DB->get_record('user', array('id' => $userid)); twf_cron_minimise_user_record($userto); } $userto->viewfullnames = array(); $userto->canpost = array(); $userto->markposts = array(); // Override the language and timezone of the "current" user, so that // mail is customised for the receiver. cron_setup_user($userto); $postsubject = get_string('digestmailsubject', 'twf', format_string($site->shortname, true)); $headerdata = new stdClass(); $headerdata->sitename = format_string($site->fullname, true); $headerdata->userprefs = $CFG->wwwroot . '/user/edit.php?id=' . $userid . '&course=' . $site->id; $posttext = get_string('digestmailheader', 'twf', $headerdata) . "\n\n"; $headerdata->userprefs = '<a target="_blank" href="' . $headerdata->userprefs . '">' . get_string('digestmailprefs', 'twf') . '</a>'; $posthtml = "<head>"; /* foreach ($CFG->stylesheets as $stylesheet) { //TODO: MDL-21120 $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n"; }*/ $posthtml .= "</head>\n<body id=\"email\">\n"; $posthtml .= '<p>' . get_string('digestmailheader', 'twf', $headerdata) . '</p><br /><hr size="1" noshade="noshade" />'; foreach ($thesediscussions as $discussionid) { core_php_time_limit::raise(120); // to be reset for each post $discussion = $discussions[$discussionid]; $twf = $twfs[$discussion->twf]; $course = $courses[$twf->course]; $cm = $coursemodules[$twf->id]; //override language cron_setup_user($userto, $course); // Fill caches if (!isset($userto->viewfullnames[$twf->id])) { $modcontext = context_module::instance($cm->id); $userto->viewfullnames[$twf->id] = has_capability('moodle/site:viewfullnames', $modcontext); } if (!isset($userto->canpost[$discussion->id])) { $modcontext = context_module::instance($cm->id); $userto->canpost[$discussion->id] = twf_user_can_post($twf, $discussion, $userto, $cm, $course, $modcontext); } $strtwfs = get_string('twfs', 'twf'); $canunsubscribe = !\mod_twf\subscriptions::is_forcesubscribed($twf); $canreply = $userto->canpost[$discussion->id]; $shortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id))); $posttext .= "\n \n"; $posttext .= '====================================================================='; $posttext .= "\n \n"; $posttext .= "{$shortname} -> {$strtwfs} -> " . format_string($twf->name, true); if ($discussion->name != $twf->name) { $posttext .= " -> " . format_string($discussion->name, true); } $posttext .= "\n"; $posttext .= $CFG->wwwroot . '/mod/twf/discuss.php?d=' . $discussion->id; $posttext .= "\n"; $posthtml .= "<p><font face=\"sans-serif\">" . "<a target=\"_blank\" href=\"{$CFG->wwwroot}/course/view.php?id={$course->id}\">{$shortname}</a> -> " . "<a target=\"_blank\" href=\"{$CFG->wwwroot}/mod/twf/index.php?id={$course->id}\">{$strtwfs}</a> -> " . "<a target=\"_blank\" href=\"{$CFG->wwwroot}/mod/twf/view.php?f={$twf->id}\">" . format_string($twf->name, true) . "</a>"; if ($discussion->name == $twf->name) { $posthtml .= "</font></p>"; } else { $posthtml .= " -> <a target=\"_blank\" href=\"{$CFG->wwwroot}/mod/twf/discuss.php?d={$discussion->id}\">" . format_string($discussion->name, true) . "</a></font></p>"; } $posthtml .= '<p>'; $postsarray = $discussionposts[$discussionid]; sort($postsarray); foreach ($postsarray as $postid) { $post = $posts[$postid]; if (array_key_exists($post->userid, $users)) { // we might know him/her already $userfrom = $users[$post->userid]; if (!isset($userfrom->idnumber)) { $userfrom = $DB->get_record('user', array('id' => $userfrom->id)); twf_cron_minimise_user_record($userfrom); } } else { if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) { twf_cron_minimise_user_record($userfrom); if ($userscount <= FORUM_CRON_USER_CACHE) { $userscount++; $users[$userfrom->id] = $userfrom; } } else { mtrace('Could not find user ' . $post->userid); continue; } } if (!isset($userfrom->groups[$twf->id])) { if (!isset($userfrom->groups)) { $userfrom->groups = array(); if (isset($users[$userfrom->id])) { $users[$userfrom->id]->groups = array(); } } $userfrom->groups[$twf->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid); if (isset($users[$userfrom->id])) { $users[$userfrom->id]->groups[$twf->id] = $userfrom->groups[$twf->id]; } } // Headers to help prevent auto-responders. $userfrom->customheaders = array("Precedence: Bulk", 'X-Auto-Response-Suppress: All', 'Auto-Submitted: auto-generated'); $maildigest = twf_get_user_maildigest_bulk($digests, $userto, $twf->id); if ($maildigest == 2) { // Subjects and link only $posttext .= "\n"; $posttext .= $CFG->wwwroot . '/mod/twf/discuss.php?d=' . $discussion->id; $by = new stdClass(); $by->name = fullname($userfrom); $by->date = userdate($post->modified); $posttext .= "\n" . format_string($post->subject, true) . ' ' . get_string("bynameondate", "twf", $by); $posttext .= "\n---------------------------------------------------------------------"; $by->name = "<a target=\"_blank\" href=\"{$CFG->wwwroot}/user/view.php?id={$userfrom->id}&course={$course->id}\">{$by->name}</a>"; $posthtml .= '<div><a target="_blank" href="' . $CFG->wwwroot . '/mod/twf/discuss.php?d=' . $discussion->id . '#p' . $post->id . '">' . format_string($post->subject, true) . '</a> ' . get_string("bynameondate", "twf", $by) . '</div>'; } else { // The full treatment $posttext .= twf_make_mail_text($course, $cm, $twf, $discussion, $post, $userfrom, $userto, true); $posthtml .= twf_make_mail_post($course, $cm, $twf, $discussion, $post, $userfrom, $userto, false, $canreply, true, false); // Create an array of postid's for this user to mark as read. if (!$CFG->twf_usermarksread) { $userto->markposts[$post->id] = $post->id; } } } $footerlinks = array(); if ($canunsubscribe) { $footerlinks[] = "<a href=\"{$CFG->wwwroot}/mod/twf/subscribe.php?id={$twf->id}\">" . get_string("unsubscribe", "twf") . "</a>"; } else { $footerlinks[] = get_string("everyoneissubscribed", "twf"); } $footerlinks[] = "<a href='{$CFG->wwwroot}/mod/twf/index.php?id={$twf->course}'>" . get_string("digestmailpost", "twf") . '</a>'; $posthtml .= "\n<div class='mdl-right'><font size=\"1\">" . implode(' ', $footerlinks) . '</font></div>'; $posthtml .= '<hr size="1" noshade="noshade" /></p>'; } $posthtml .= '</body>'; if (empty($userto->mailformat) || $userto->mailformat != 1) { // This user DOESN'T want to receive HTML $posthtml = ''; } $attachment = $attachname = ''; // Directly email twf digests rather than sending them via messaging, use the // site shortname as 'from name', the noreply address will be used by email_to_user. $mailresult = email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml, $attachment, $attachname); if (!$mailresult) { mtrace("ERROR: mod/twf/cron.php: Could not send out digest mail to user {$userto->id} " . "({$userto->email})... not trying again."); } else { mtrace("success."); $usermailcount++; // Mark post as read if twf_usermarksread is set off twf_tp_mark_posts_read($userto, $userto->markposts); } } } /// We have finishied all digest emails, update $CFG->digestmailtimelast set_config('digestmailtimelast', $timenow); } cron_setup_user(); if (!empty($usermailcount)) { mtrace(get_string('digestsentusers', 'twf', $usermailcount)); } if (!empty($CFG->twf_lastreadclean)) { $timenow = time(); if ($CFG->twf_lastreadclean + 24 * 3600 < $timenow) { set_config('twf_lastreadclean', $timenow); mtrace('Removing old twf read tracking info...'); twf_tp_clean_read_records(); } } else { set_config('twf_lastreadclean', time()); } return true; }