Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>./*** A scheduled task for forum cron.** @package mod_forum* @copyright 2014 Dan Poltawski <dan@moodle.com>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/namespace mod_forum\task;defined('MOODLE_INTERNAL') || die();require_once($CFG->dirroot . '/mod/forum/lib.php');/*** The main scheduled task for the forum.** @package mod_forum* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class cron_task extends \core\task\scheduled_task {// Use the logging trait to get some nice, juicy, logging.use \core\task\logging_trait;/*** @var The list of courses which contain posts to be sent.*/protected $courses = [];/*** @var The list of forums which contain posts to be sent.*/protected $forums = [];/*** @var The list of discussions which contain posts to be sent.*/protected $discussions = [];/*** @var The list of posts to be sent.*/protected $posts = [];/*** @var The list of post authors.*/protected $users = [];/*** @var The list of subscribed users.*/protected $subscribedusers = [];/*** @var The list of digest users.*/protected $digestusers = [];/*** @var The list of adhoc data for sending.*/protected $adhocdata = [];/*** Get a descriptive name for this task (shown to admins).** @return string*/public function get_name() {return get_string('crontask', 'mod_forum');}/*** Execute the scheduled task.*/public function execute() {global $CFG, $DB;$timenow = time();// Delete any really old posts in the digest queue.$weekago = $timenow - (7 * 24 * 3600);$this->log_start("Removing old digest records from 7 days ago.");$DB->delete_records_select('forum_queue', "timemodified < ?", array($weekago));$this->log_finish("Removed all old digest records.");$endtime = $timenow - $CFG->maxeditingtime;$starttime = $endtime - (2 * DAYSECS);$this->log_start("Fetching unmailed posts.");if (!$posts = $this->get_unmailed_posts($starttime, $endtime, $timenow)) {$this->log_finish("No posts found.", 1);return false;}$this->log_finish("Done");// Process post data and turn into adhoc tasks.$this->process_post_data($posts);// Mark posts as read.list($in, $params) = $DB->get_in_or_equal(array_keys($posts));$DB->set_field_select('forum_posts', 'mailed', 1, "id {$in}", $params);}/*** Process all posts and convert to appropriated hoc tasks.** @param \stdClass[] $posts*/protected function process_post_data($posts) {$discussionids = [];$forumids = [];$courseids = [];$this->log_start("Processing post information");$start = microtime(true);foreach ($posts as $id => $post) {$discussionids[$post->discussion] = true;$forumids[$post->forum] = true;$courseids[$post->course] = true;$this->add_data_for_post($post);$this->posts[$id] = $post;}$this->log_finish(sprintf("Processed %s posts", count($this->posts)));if (empty($this->posts)) {$this->log("No posts found. Returning early.");return;}// Please note, this order is intentional.// The forum cache makes use of the course.$this->log_start("Filling caches");$start = microtime(true);$this->log_start("Filling course cache", 1);$this->fill_course_cache(array_keys($courseids));$this->log_finish("Done", 1);$this->log_start("Filling forum cache", 1);$this->fill_forum_cache(array_keys($forumids));$this->log_finish("Done", 1);$this->log_start("Filling discussion cache", 1);$this->fill_discussion_cache(array_keys($discussionids));$this->log_finish("Done", 1);$this->log_start("Filling user subscription cache", 1);$this->fill_user_subscription_cache();$this->log_finish("Done", 1);$this->log_start("Filling digest cache", 1);$this->fill_digest_cache();$this->log_finish("Done", 1);$this->log_finish("All caches filled");$this->log_start("Queueing user tasks.");$this->queue_user_tasks();$this->log_finish("All tasks queued.");}/*** Fill the course cache.** @param int[] $courseids*/protected function fill_course_cache($courseids) {global $DB;list($in, $params) = $DB->get_in_or_equal($courseids);$this->courses = $DB->get_records_select('course', "id $in", $params);}/*** Fill the forum cache.** @param int[] $forumids*/protected function fill_forum_cache($forumids) {global $DB;$requiredfields = ['id','course','forcesubscribe','type',];list($in, $params) = $DB->get_in_or_equal($forumids);$this->forums = $DB->get_records_select('forum', "id $in", $params, '', implode(', ', $requiredfields));foreach ($this->forums as $id => $forum) {\mod_forum\subscriptions::fill_subscription_cache($id);\mod_forum\subscriptions::fill_discussion_subscription_cache($id);}}/*** Fill the discussion cache.** @param int[] $discussionids*/protected function fill_discussion_cache($discussionids) {global $DB;if (empty($discussionids)) {$this->discussions = [];} else {$requiredfields = ['id','groupid','firstpost','timestart','timeend',];list($in, $params) = $DB->get_in_or_equal($discussionids);$this->discussions = $DB->get_records_select('forum_discussions', "id $in", $params, '', implode(', ', $requiredfields));}}/*** Fill the cache of user digest preferences.*/protected function fill_digest_cache() {global $DB;if (empty($this->users)) {return;}// Get the list of forum subscriptions for per-user per-forum maildigest settings.list($in, $params) = $DB->get_in_or_equal(array_keys($this->users));$digestspreferences = $DB->get_recordset_select('forum_digests', "userid $in", $params, '', 'id, userid, forum, maildigest');foreach ($digestspreferences as $digestpreference) {if (!isset($this->digestusers[$digestpreference->forum])) {$this->digestusers[$digestpreference->forum] = [];}$this->digestusers[$digestpreference->forum][$digestpreference->userid] = $digestpreference->maildigest;}$digestspreferences->close();}/*** Add dsta for the current forum post to the structure of adhoc data.** @param \stdClass $post*/protected function add_data_for_post($post) {if (!isset($this->adhocdata[$post->course])) {$this->adhocdata[$post->course] = [];}if (!isset($this->adhocdata[$post->course][$post->forum])) {$this->adhocdata[$post->course][$post->forum] = [];}if (!isset($this->adhocdata[$post->course][$post->forum][$post->discussion])) {$this->adhocdata[$post->course][$post->forum][$post->discussion] = [];}$this->adhocdata[$post->course][$post->forum][$post->discussion][$post->id] = $post->id;}/*** Fill the cache of user subscriptions.*/protected function fill_user_subscription_cache() {foreach ($this->forums as $forum) {$cm = get_fast_modinfo($this->courses[$forum->course])->instances['forum'][$forum->id];$modcontext = \context_module::instance($cm->id);$this->subscribedusers[$forum->id] = [];if ($users = \mod_forum\subscriptions::fetch_subscribed_users($forum, 0, $modcontext, 'u.id, u.maildigest', true)) {foreach ($users as $user) {// This user is subscribed to this forum.$this->subscribedusers[$forum->id][$user->id] = $user->id;if (!isset($this->users[$user->id])) {// Store minimal user info.$this->users[$user->id] = $user;}}// Release memory.unset($users);}}}/*** Queue the user tasks.*/protected function queue_user_tasks() {global $CFG, $DB;$timenow = time();$sitetimezone = \core_date::get_server_timezone();$counts = ['digests' => 0,'individuals' => 0,'users' => 0,'ignored' => 0,'messages' => 0,];$this->log("Processing " . count($this->users) . " users", 1);foreach ($this->users as $user) {$usercounts = ['digests' => 0,'messages' => 0,];$send = false;// Setup this user so that the capabilities are cached, and environment matches receiving user.\core\cron::setup_user($user);list($individualpostdata, $digestpostdata) = $this->fetch_posts_for_user($user);if (!empty($digestpostdata)) {// Insert all of the records for the digest.$DB->insert_records('forum_queue', $digestpostdata);$servermidnight = usergetmidnight($timenow, $sitetimezone);$digesttime = $servermidnight + ($CFG->digestmailtime * 3600);if ($digesttime < $timenow) {// Digest time is in the past. Get a new time for tomorrow.$servermidnight = usergetmidnight($timenow + DAYSECS, $sitetimezone);$digesttime = $servermidnight + ($CFG->digestmailtime * 3600);}$task = new \mod_forum\task\send_user_digests();$task->set_userid($user->id);$task->set_component('mod_forum');$task->set_custom_data(['servermidnight' => $servermidnight]);$task->set_next_run_time($digesttime);\core\task\manager::reschedule_or_queue_adhoc_task($task);$usercounts['digests']++;$send = true;}if (!empty($individualpostdata)) {$usercounts['messages'] += count($individualpostdata);$task = new \mod_forum\task\send_user_notifications();$task->set_userid($user->id);$task->set_custom_data($individualpostdata);$task->set_component('mod_forum');\core\task\manager::queue_adhoc_task($task);$counts['individuals']++;$send = true;}if ($send) {$counts['users']++;$counts['messages'] += $usercounts['messages'];$counts['digests'] += $usercounts['digests'];} else {$counts['ignored']++;}$this->log(sprintf("Queued %d digests and %d messages for %s",$usercounts['digests'],$usercounts['messages'],$user->id), 2);}$this->log(sprintf("Queued %d digests, and %d individual tasks for %d post mails. " ."Unique users: %d (%d ignored)",$counts['digests'],$counts['individuals'],$counts['messages'],$counts['users'],$counts['ignored']), 1);}/*** Fetch posts for this user.** @param \stdClass $user The user to fetch posts for.*/protected function fetch_posts_for_user($user) {// We maintain a mapping of user groups for each forum.$usergroups = [];$digeststructure = [];$poststructure = $this->adhocdata;$poststosend = [];foreach ($poststructure as $courseid => $forumids) {$course = $this->courses[$courseid];foreach ($forumids as $forumid => $discussionids) {$forum = $this->forums[$forumid];$maildigest = forum_get_user_maildigest_bulk($this->digestusers, $user, $forumid);if (!isset($this->subscribedusers[$forumid][$user->id])) {// This user has no subscription of any kind to this forum.// Do not send them any posts at all.unset($poststructure[$courseid][$forumid]);continue;}$subscriptiontime = \mod_forum\subscriptions::fetch_discussion_subscription($forum->id, $user->id);$cm = get_fast_modinfo($course)->instances['forum'][$forumid];foreach ($discussionids as $discussionid => $postids) {$discussion = $this->discussions[$discussionid];if (!\mod_forum\subscriptions::is_subscribed($user->id, $forum, $discussionid, $cm)) {// The user does not subscribe to this forum as a whole, or to this specific discussion.unset($poststructure[$courseid][$forumid][$discussionid]);continue;}if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {// This discussion has a groupmode set (SEPARATEGROUPS or VISIBLEGROUPS).// Check whether the user can view it based on their groups.if (!isset($usergroups[$forum->id])) {$usergroups[$forum->id] = groups_get_all_groups($courseid, $user->id, $cm->groupingid);}if (!isset($usergroups[$forum->id][$discussion->groupid])) {// This user is not a member of this group, or the group no longer exists.$modcontext = \context_module::instance($cm->id);if (!has_capability('moodle/site:accessallgroups', $modcontext, $user)) {// This user does not have the accessallgroups and is not a member of the group.// Do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS.unset($poststructure[$courseid][$forumid][$discussionid]);continue;}}}foreach ($postids as $postid) {$post = $this->posts[$postid];if ($subscriptiontime) {// Skip posts if the user subscribed to the discussion after it was created.$subscribedafter = isset($subscriptiontime[$post->discussion]);$subscribedafter = $subscribedafter && ($subscriptiontime[$post->discussion] > $post->created);if ($subscribedafter) {// The user subscribed to the discussion/forum after this post was created.unset($poststructure[$courseid][$forumid][$discussionid][$postid]);continue;}}if ($maildigest > 0) {// This user wants the mails to be in digest form.$digeststructure[] = (object) ['userid' => $user->id,'discussionid' => $discussion->id,'postid' => $post->id,'timemodified' => $post->created,];unset($poststructure[$courseid][$forumid][$discussionid][$postid]);continue;} else {// Add this post to the list of postids to be sent.$poststosend[] = $postid;}}}if (empty($poststructure[$courseid][$forumid])) {// This user is not subscribed to any discussions in this forum at all.unset($poststructure[$courseid][$forumid]);continue;}}if (empty($poststructure[$courseid])) {// This user is not subscribed to any forums in this course.unset($poststructure[$courseid]);}}return [$poststosend, $digeststructure];}/*** Returns a list of all new posts that have not been mailed yet** @param int $starttime posts created after this time* @param int $endtime posts created before this* @param int $now used for timed discussions only* @return array*/protected function get_unmailed_posts($starttime, $endtime, $now = null) {global $CFG, $DB;$params = array();$params['mailed'] = FORUM_MAILED_PENDING;$params['ptimestart'] = $starttime;$params['ptimeend'] = $endtime;$params['mailnow'] = 1;if (!empty($CFG->forum_enabletimedposts)) {if (empty($now)) {$now = time();}$selectsql = "AND (p.created >= :ptimestart OR d.timestart >= :pptimestart)";$params['pptimestart'] = $starttime;$timedsql = "AND (d.timestart < :dtimestart AND (d.timeend = 0 OR d.timeend > :dtimeend))";$params['dtimestart'] = $now;$params['dtimeend'] = $now;} else {$timedsql = "";$selectsql = "AND p.created >= :ptimestart";}return $DB->get_records_sql("SELECTp.id,p.discussion,d.forum,d.course,p.created,p.parent,p.useridFROM {forum_posts} pJOIN {forum_discussions} d ON d.id = p.discussionWHERE p.mailed = :mailed$selectsqlAND (p.created < :ptimeend OR p.mailnow = :mailnow)$timedsqlORDER BY p.modified ASC",$params);}}