芝麻web文件管理V1.00
编辑当前文件:/home2/sdektunc/public_html/cepali/mod/forum/tests/mail_test.php
. /** * The forum module mail generation tests. * * @package mod_forum * @category external * @copyright 2013 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot . '/mod/forum/lib.php'); require_once(__DIR__ . '/cron_trait.php'); require_once(__DIR__ . '/generator_trait.php'); class mod_forum_mail_testcase extends advanced_testcase { // Make use of the cron tester trait. use mod_forum_tests_cron_trait; // Make use of the test generator trait. use mod_forum_tests_generator_trait; /** * @var \phpunit_message_sink */ protected $messagesink; /** * @var \phpunit_mailer_sink */ protected $mailsink; public function setUp() { global $CFG; // We must clear the subscription caches. This has to be done both before each test, and after in case of other // tests using these functions. \mod_forum\subscriptions::reset_forum_cache(); \mod_forum\subscriptions::reset_discussion_cache(); // Messaging is not compatible with transactions... $this->preventResetByRollback(); // Catch all messages. $this->messagesink = $this->redirectMessages(); $this->mailsink = $this->redirectEmails(); // Forcibly reduce the maxeditingtime to a second in the past to // ensure that messages are sent out. $CFG->maxeditingtime = -1; } public function tearDown() { // We must clear the subscription caches. This has to be done both before each test, and after in case of other // tests using these functions. \mod_forum\subscriptions::reset_forum_cache(); $this->messagesink->clear(); $this->messagesink->close(); unset($this->messagesink); $this->mailsink->clear(); $this->mailsink->close(); unset($this->mailsink); } /** * Perform message inbound setup for the mod_forum reply handler. */ protected function helper_spoof_message_inbound_setup() { global $CFG, $DB; // Setup the default Inbound Message mailbox settings. $CFG->messageinbound_domain = 'example.com'; $CFG->messageinbound_enabled = true; // Must be no longer than 15 characters. $CFG->messageinbound_mailbox = 'moodlemoodle123'; $record = $DB->get_record('messageinbound_handlers', array('classname' => '\mod_forum\message\inbound\reply_handler')); $record->enabled = true; $record->id = $DB->update_record('messageinbound_handlers', $record); } public function test_cron_message_includes_courseid() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $expect = [ 'author' => (object) [ 'userid' => $author->id, 'messages' => 1, ], 'recipient' => (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->messagesink->close(); $this->eventsink = $this->redirectEvents(); $this->send_notifications_and_assert($author, [$post]); $events = $this->eventsink->get_events(); $event = reset($events); $this->assertEquals($course->id, $event->other['courseid']); $this->send_notifications_and_assert($recipient, [$post]); } public function test_forced_subscription() { global $DB; $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create users enrolled in the course as students. list($author, $recipient, $unconfirmed, $deleted) = $this->helper_create_users($course, 4); // Make the third user unconfirmed (thence inactive) to make sure it does not break the notifications. $DB->set_field('user', 'confirmed', 0, ['id' => $unconfirmed->id]); // Mark the fourth user as deleted to make sure it does not break the notifications. $DB->set_field('user', 'deleted', 1, ['id' => $deleted->id]); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $expect = [ (object) [ 'userid' => $author->id, 'messages' => 1, ], (object) [ 'userid' => $recipient->id, 'messages' => 1, ], (object) [ 'userid' => $unconfirmed->id, 'messages' => 0, ], (object) [ 'userid' => $deleted->id, 'messages' => 0, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, [$post]); $this->send_notifications_and_assert($recipient, [$post]); $this->send_notifications_and_assert($unconfirmed, []); $this->send_notifications_and_assert($deleted, []); } /** * Ensure that for a forum with subscription disabled that standard users will not receive posts. */ public function test_subscription_disabled_standard_users() { global $DB; $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); // Run cron and check that the expected number of users received the notification. $expect = [ (object) [ 'userid' => $author->id, 'messages' => 0, ], (object) [ 'userid' => $recipient->id, 'messages' => 0, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, []); } /** * Ensure that for a forum with subscription disabled that a user subscribed to the forum will receive the post. */ public function test_subscription_disabled_user_subscribed_forum() { global $DB; $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // A user with the manageactivities capability within the course can subscribe. $roleids = $DB->get_records_menu('role', null, '', 'shortname, id'); assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleids['student'], context_course::instance($course->id)); // Suscribe the recipient only. \mod_forum\subscriptions::subscribe_user($recipient->id, $forum); $this->assertEquals(1, $DB->count_records('forum_subscriptions', array( 'userid' => $recipient->id, 'forum' => $forum->id, ))); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); // Run cron and check that the expected number of users received the notification. $expect = [ 'author' => (object) [ 'userid' => $author->id, ], 'recipient' => (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, [$post]); } /** * Ensure that for a forum with subscription disabled that a user subscribed to the discussion will receive the * post. */ public function test_subscription_disabled_user_subscribed_discussion() { global $DB; $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // A user with the manageactivities capability within the course can subscribe. $roleids = $DB->get_records_menu('role', null, '', 'shortname, id'); assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleids['student'], context_course::instance($course->id)); // Run cron and check that the expected number of users received the notification. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); // Subscribe the user to the discussion. \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion); $this->helper_update_subscription_time($recipient, $discussion, -60); // Run cron and check that the expected number of users received the notification. $expect = [ 'author' => (object) [ 'userid' => $author->id, ], 'recipient' => (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, [$post]); } /** * Ensure that for a forum with automatic subscription that users receive posts. */ public function test_automatic() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $expect = [ (object) [ 'userid' => $author->id, 'messages' => 1, ], (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, [$post]); $this->send_notifications_and_assert($recipient, [$post]); } /** * Ensure that private replies are not sent to users with an automatic subscription unless they are an expected * recipient. */ public function test_automatic_with_private_reply() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('forum', [ 'course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE, ]); [$student, $otherstudent] = $this->helper_create_users($course, 2, 'student'); [$teacher, $otherteacher] = $this->helper_create_users($course, 2, 'teacher'); [$discussion, $post] = $this->helper_post_to_forum($forum, $student); $reply = $this->helper_post_to_discussion($forum, $discussion, $teacher, [ 'privatereplyto' => $student->id, ]); // The private reply is queued to all messages as reply visibility may change between queueing, and sending. $expect = [ (object) [ 'userid' => $student->id, 'messages' => 2, ], (object) [ 'userid' => $otherstudent->id, 'messages' => 2, ], (object) [ 'userid' => $teacher->id, 'messages' => 2, ], (object) [ 'userid' => $otherteacher->id, 'messages' => 2, ], ]; $this->queue_tasks_and_assert($expect); // The actual messages sent will respect private replies. $this->send_notifications_and_assert($student, [$post, $reply]); $this->send_notifications_and_assert($teacher, [$post, $reply]); $this->send_notifications_and_assert($otherteacher, [$post, $reply]); $this->send_notifications_and_assert($otherstudent, [$post]); } public function test_optional() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $expect = [ (object) [ 'userid' => $author->id, 'messages' => 0, ], (object) [ 'userid' => $recipient->id, 'messages' => 0, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, []); } public function test_automatic_with_unsubscribed_user() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Unsubscribe the 'author' user from the forum. \mod_forum\subscriptions::unsubscribe_user($author->id, $forum); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $expect = [ (object) [ 'userid' => $author->id, 'messages' => 0, ], (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, [$post]); } public function test_optional_with_subscribed_user() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Subscribe the 'recipient' user from the forum. \mod_forum\subscriptions::subscribe_user($recipient->id, $forum); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $expect = [ (object) [ 'userid' => $author->id, 'messages' => 0, ], (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, [$post]); } public function test_automatic_with_unsubscribed_discussion() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); // Unsubscribe the 'author' user from the discussion. \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion); $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id)); $this->assertTrue(\mod_forum\subscriptions::is_subscribed($recipient->id, $forum, $discussion->id)); $expect = [ (object) [ 'userid' => $author->id, 'messages' => 0, ], (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, [$post]); } public function test_optional_with_subscribed_discussion() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $this->helper_update_post_time($post, -90); // Subscribe the 'recipient' user to the discussion. \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion); $this->helper_update_subscription_time($recipient, $discussion, -60); // Initially we don't expect any user to receive this post as you cannot subscribe to a discussion until after // you have read it. $expect = [ (object) [ 'userid' => $author->id, 'messages' => 0, ], (object) [ 'userid' => $recipient->id, 'messages' => 0, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, []); // Have a user reply to the discussion. $reply = $this->helper_post_to_discussion($forum, $discussion, $author); $this->helper_update_post_time($reply, -30); // We expect only one user to receive this post. $expect = [ (object) [ 'userid' => $author->id, 'messages' => 0, ], (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, [$reply]); } public function test_optional_with_subscribed_discussion_and_post() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $this->helper_update_post_time($post, -90); // Have a user reply to the discussion before we subscribed. $reply = $this->helper_post_to_discussion($forum, $discussion, $author); $this->helper_update_post_time($reply, -75); // Subscribe the 'recipient' user to the discussion. \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion); $this->helper_update_subscription_time($recipient, $discussion, -60); // Have a user reply to the discussion. $reply = $this->helper_post_to_discussion($forum, $discussion, $author); $this->helper_update_post_time($reply, -30); // We expect only one user to receive this post. // The original post won't be received as it was written before the user subscribed. $expect = [ (object) [ 'userid' => $author->id, 'messages' => 0, ], (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, [$reply]); } public function test_automatic_with_subscribed_discussion_in_unsubscribed_forum() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $this->helper_update_post_time($post, -90); // Unsubscribe the 'author' user from the forum. \mod_forum\subscriptions::unsubscribe_user($author->id, $forum); // Then re-subscribe them to the discussion. \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion); $this->helper_update_subscription_time($author, $discussion, -60); $expect = [ (object) [ 'userid' => $author->id, 'messages' => 0, ], (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, [$post]); // Now post a reply to the original post. $reply = $this->helper_post_to_discussion($forum, $discussion, $author); $this->helper_update_post_time($reply, -30); $expect = [ (object) [ 'userid' => $author->id, 'messages' => 1, ], (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, [$reply]); $this->send_notifications_and_assert($recipient, [$reply]); } public function test_optional_with_unsubscribed_discussion_in_subscribed_forum() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); // Unsubscribe the 'recipient' user from the discussion. \mod_forum\subscriptions::subscribe_user($recipient->id, $forum); // Then unsubscribe them from the discussion. \mod_forum\subscriptions::unsubscribe_user_from_discussion($recipient->id, $discussion); // We don't expect any users to receive this post. $expect = [ (object) [ 'userid' => $author->id, 'messages' => 0, ], (object) [ 'userid' => $recipient->id, 'messages' => 0, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, []); } /** * Test that a user unsubscribed from a forum who has subscribed to a discussion, only receives posts made after * they subscribed to the discussion. */ public function test_forum_discussion_subscription_forum_unsubscribed_discussion_subscribed_after_post() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); $expectedmessages = array(); // Create a user enrolled in the course as a student. list($author) = $this->helper_create_users($course, 1); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $this->helper_update_post_time($post, -90); $expectedmessages[] = array( 'id' => $post->id, 'subject' => $post->subject, 'count' => 0, ); // Then subscribe the user to the discussion. $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion)); $this->helper_update_subscription_time($author, $discussion, -60); // Then post a reply to the first discussion. $reply = $this->helper_post_to_discussion($forum, $discussion, $author); $this->helper_update_post_time($reply, -30); $expect = [ (object) [ 'userid' => $author->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, [$reply]); } public function test_subscription_by_inactive_users() { global $DB; $this->resetAfterTest(true); $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $u1, $u2, $u3) = $this->helper_create_users($course, 4); // Subscribe the three users to the forum. \mod_forum\subscriptions::subscribe_user($u1->id, $forum); \mod_forum\subscriptions::subscribe_user($u2->id, $forum); \mod_forum\subscriptions::subscribe_user($u3->id, $forum); // Make the first user inactive - suspended. $DB->set_field('user', 'suspended', 1, ['id' => $u1->id]); // Make the second user inactive - unable to log in. $DB->set_field('user', 'auth', 'nologin', ['id' => $u2->id]); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $expect = [ (object) [ 'userid' => $u1->id, 'messages' => 0, ], (object) [ 'userid' => $u2->id, 'messages' => 0, ], (object) [ 'userid' => $u3->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($u1, []); $this->send_notifications_and_assert($u2, []); $this->send_notifications_and_assert($u3, [$post]); } public function test_forum_message_inbound_multiple_posts() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create a user enrolled in the course as a student. list($author) = $this->helper_create_users($course, 1); $expectedmessages = array(); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $this->helper_update_post_time($post, -90); $expectedmessages[] = (object) [ 'id' => $post->id, 'subject' => $post->subject, 'count' => 0, ]; // Then post a reply to the first discussion. $reply = $this->helper_post_to_discussion($forum, $discussion, $author); $this->helper_update_post_time($reply, -60); $expectedmessages[] = (object) [ 'id' => $reply->id, 'subject' => $reply->subject, 'count' => 1, ]; // Ensure that messageinbound is enabled and configured for the forum handler. $this->helper_spoof_message_inbound_setup(); $author->emailstop = '0'; set_user_preference('message_provider_mod_forum_posts_loggedoff', 'email', $author); set_user_preference('message_provider_mod_forum_posts_loggedin', 'email', $author); // Run cron and check that the expected number of users received the notification. // Clear the mailsink, and close the messagesink. $this->mailsink->clear(); $this->messagesink->close(); $expect = [ 'author' => (object) [ 'userid' => $author->id, 'messages' => count($expectedmessages), ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, $expectedmessages); $messages = $this->mailsink->get_messages(); // There should be the expected number of messages. $this->assertEquals(2, count($messages)); foreach ($messages as $message) { $this->assertRegExp('/Reply-To: moodlemoodle123\+[^@]*@example.com/', $message->header); } } public function test_long_subject() { $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create a user enrolled in the course as student. list($author) = $this->helper_create_users($course, 1); // Post a discussion to the forum. $subject = 'This is the very long forum post subject that somebody was very kind of leaving, it is intended to check if long subject comes in mail correctly. Thank you.'; $a = (object)array('courseshortname' => $course->shortname, 'forumname' => $forum->name, 'subject' => $subject); $expectedsubject = get_string('postmailsubject', 'forum', $a); list($discussion, $post) = $this->helper_post_to_forum($forum, $author, array('name' => $subject)); // Run cron and check that the expected number of users received the notification. $expect = [ 'author' => (object) [ 'userid' => $author->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, [$post]); $messages = $this->messagesink->get_messages(); $message = reset($messages); $this->assertEquals($author->id, $message->useridfrom); $this->assertEquals($expectedsubject, $message->subject); } /** * Test inital email and reply email subjects */ public function test_subjects() { $this->resetAfterTest(true); $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); list($author) = $this->helper_create_users($course, 1); list($commenter) = $this->helper_create_users($course, 1); $strre = get_string('re', 'forum'); // New posts should not have Re: in the subject. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $expect = [ 'author' => (object) [ 'userid' => $author->id, 'messages' => 1, ], 'commenter' => (object) [ 'userid' => $commenter->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, [$post]); $this->send_notifications_and_assert($commenter, [$post]); $messages = $this->messagesink->get_messages(); $this->assertNotContains($strre, $messages[0]->subject); $this->messagesink->clear(); // Replies should have Re: in the subject. $reply = $this->helper_post_to_discussion($forum, $discussion, $commenter); $expect = [ 'author' => (object) [ 'userid' => $author->id, 'messages' => 1, ], 'commenter' => (object) [ 'userid' => $commenter->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($commenter, [$reply]); $this->send_notifications_and_assert($author, [$reply]); $messages = $this->messagesink->get_messages(); $this->assertContains($strre, $messages[0]->subject); $this->assertContains($strre, $messages[1]->subject); } /** * dataProvider for test_forum_post_email_templates(). */ public function forum_post_email_templates_provider() { // Base information, we'll build variations based on it. $base = array( 'user' => array('firstname' => 'Love', 'lastname' => 'Moodle', 'mailformat' => 0, 'maildigest' => 0), 'course' => array('shortname' => '101', 'fullname' => 'Moodle 101'), 'forums' => array( array( 'name' => 'Moodle Forum', 'forumposts' => array( array( 'name' => 'Hello Moodle', 'message' => 'Welcome to Moodle', 'messageformat' => FORMAT_MOODLE, 'attachments' => array( array( 'filename' => 'example.txt', 'filecontents' => 'Basic information about the course' ), ), ), ), ), ), 'expectations' => array( array( 'subject' => '.*101.*Hello', 'contents' => array( '~{$a', '~&(amp|lt|gt|quot|\#039);(?!course)', 'Attachment example.txt:' . PHP_EOL . 'https://www.example.com/moodle/pluginfile.php/\d*/mod_forum/attachment/\d*/example.txt' . PHP_EOL, 'Hello Moodle', 'Moodle Forum', 'Welcome.*Moodle', 'Love Moodle', '1\d1' ), ), ), ); // Build the text cases. $textcases = array('Text mail without ampersands, quotes or lt/gt' => array('data' => $base)); // Single and double quotes everywhere. $newcase = $base; $newcase['user']['lastname'] = 'Moodle\'"'; $newcase['course']['shortname'] = '101\'"'; $newcase['forums'][0]['name'] = 'Moodle Forum\'"'; $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle\'"'; $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle\'"'; $newcase['expectations'][0]['contents'] = array( 'Attachment example.txt:', '~{\$a', '~&(quot|\#039);', 'Love Moodle\'', '101\'', 'Moodle Forum\'"', 'Hello Moodle\'"', 'Welcome to Moodle\'"'); $textcases['Text mail with quotes everywhere'] = array('data' => $newcase); // Lt and gt everywhere. This case is completely borked because format_string() // strips tags with $CFG->formatstringstriptags and also escapes < and > (correct // for web presentation but not for text email). See MDL-19829. $newcase = $base; $newcase['user']['lastname'] = 'Moodle>'; $newcase['course']['shortname'] = '101>'; $newcase['forums'][0]['name'] = 'Moodle Forum>'; $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle>'; $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle>'; $newcase['expectations'][0]['contents'] = array( 'Attachment example.txt:', '~{\$a', '~>', 'Love Moodle>', '101>', 'Moodle Forum>', 'Hello Moodle>', 'Welcome to Moodle>'); $textcases['Text mail with gt and lt everywhere'] = array('data' => $newcase); // Ampersands everywhere. This case is completely borked because format_string() // escapes ampersands (correct for web presentation but not for text email). See MDL-19829. $newcase = $base; $newcase['user']['lastname'] = 'Moodle&'; $newcase['course']['shortname'] = '101&'; $newcase['forums'][0]['name'] = 'Moodle Forum&'; $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle&'; $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle&'; $newcase['expectations'][0]['contents'] = array( 'Attachment example.txt:', '~{\$a', '~&', 'Love Moodle&', '101&', 'Moodle Forum&', 'Hello Moodle&', 'Welcome to Moodle&'); $textcases['Text mail with ampersands everywhere'] = array('data' => $newcase); // Text+image message i.e. @@PLUGINFILE@@ token handling. $newcase = $base; $newcase['forums'][0]['forumposts'][0]['name'] = 'Text and image'; $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle, ' .'@@PLUGINFILE@@/Screen%20Shot%202016-03-22%20at%205.54.36%20AM%20%281%29.png !'; $newcase['expectations'][0]['subject'] = '.*101.*Text and image'; $newcase['expectations'][0]['contents'] = array( '~{$a', '~&(amp|lt|gt|quot|\#039);(?!course)', 'Attachment example.txt:' . PHP_EOL . 'https://www.example.com/moodle/pluginfile.php/\d*/mod_forum/attachment/\d*/example.txt' . PHP_EOL , 'Text and image', 'Moodle Forum', 'Welcome to Moodle, *' . PHP_EOL . '.*' .'https://www.example.com/moodle/pluginfile.php/\d+/mod_forum/post/\d+/' .'Screen%20Shot%202016-03-22%20at%205\.54\.36%20AM%20%281%29\.png *' . PHP_EOL . '.*!', 'Love Moodle', '1\d1'); $textcases['Text mail with text+image message i.e. @@PLUGINFILE@@ token handling'] = array('data' => $newcase); // Now the html cases. $htmlcases = array(); // New base for html cases, no quotes, lts, gts or ampersands. $htmlbase = $base; $htmlbase['user']['mailformat'] = 1; $htmlbase['expectations'][0]['contents'] = array( '~{\$a', '~&(amp|lt|gt|quot|\#039);(?!course|lang|version|iosappid|androidappid)', '
( *\n *)?
\n.*Hello Moodle', '>Moodle Forum', '>Welcome.*Moodle', '>Love Moodle', '>1\d1'); $htmlcases['HTML mail without ampersands, quotes or lt/gt'] = array('data' => $htmlbase); // Single and double quotes, lt and gt, ampersands everywhere. $newcase = $htmlbase; $newcase['user']['lastname'] = 'Moodle\'">&'; $newcase['course']['shortname'] = '101\'">&'; $newcase['forums'][0]['name'] = 'Moodle Forum\'">&'; $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle\'">&'; $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle\'">&'; $newcase['expectations'][0]['contents'] = array( '~{\$a', '~&(amp|lt|gt|quot|\#039);', '
( *\n *)?
\n.*Hello Moodle\'">&', '>Moodle Forum\'">&', '>Welcome.*Moodle\'">&', '>Love Moodle&\#039;">&', '>101\'">&'); $htmlcases['HTML mail with quotes, gt, lt and ampersand everywhere'] = array('data' => $newcase); // Text+image message i.e. @@PLUGINFILE@@ token handling. $newcase = $htmlbase; $newcase['forums'][0]['forumposts'][0]['name'] = 'HTML text and image'; $newcase['forums'][0]['forumposts'][0]['message'] = '
Welcome to Moodle, ' .'
!
'; $newcase['expectations'][0]['subject'] = '.*101.*HTML text and image'; $newcase['expectations'][0]['contents'] = array( '~{\$a', '~&(amp|lt|gt|quot|\#039);(?!course|lang|version|iosappid|androidappid)', '
( *\n *)?
\n.*HTML text and image', '>Moodle Forum', '
Welcome to Moodle, ' .'
!
', '>Love Moodle', '>1\d1'); $htmlcases['HTML mail with text+image message i.e. @@PLUGINFILE@@ token handling'] = array('data' => $newcase); return $textcases + $htmlcases; } /** * Verify forum emails body using templates to generate the expected results. * * @dataProvider forum_post_email_templates_provider * @param array $data provider samples. */ public function test_forum_post_email_templates($data) { global $DB; $this->resetAfterTest(); // Create the course, with the specified options. $options = array(); foreach ($data['course'] as $option => $value) { $options[$option] = $value; } $course = $this->getDataGenerator()->create_course($options); // Create the user, with the specified options and enrol in the course. $options = array(); foreach ($data['user'] as $option => $value) { $options[$option] = $value; } $user = $this->getDataGenerator()->create_user($options); $this->getDataGenerator()->enrol_user($user->id, $course->id); // Create forums, always force susbscribed (for easy), with the specified options. $posts = array(); foreach ($data['forums'] as $dataforum) { $forumposts = isset($dataforum['forumposts']) ? $dataforum['forumposts'] : array(); unset($dataforum['forumposts']); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); foreach ($dataforum as $option => $value) { $options[$option] = $value; } $forum = $this->getDataGenerator()->create_module('forum', $options); // Create posts, always for immediate delivery (for easy), with the specified options. foreach ($forumposts as $forumpost) { $attachments = isset($forumpost['attachments']) ? $forumpost['attachments'] : array(); unset($forumpost['attachments']); $postoptions = array('course' => $course->id, 'forum' => $forum->id, 'userid' => $user->id, 'mailnow' => 1, 'attachment' => !empty($attachments)); foreach ($forumpost as $option => $value) { $postoptions[$option] = $value; } list($discussion, $post) = $this->helper_post_to_forum($forum, $user, $postoptions); $posts[$post->subject] = $post; // Need this to verify cron output. // Add the attachments to the post. if ($attachments) { $fs = get_file_storage(); foreach ($attachments as $attachment) { $filerecord = array( 'contextid' => context_module::instance($forum->cmid)->id, 'component' => 'mod_forum', 'filearea' => 'attachment', 'itemid' => $post->id, 'filepath' => '/', 'filename' => $attachment['filename'] ); $fs->create_file_from_string($filerecord, $attachment['filecontents']); } $DB->set_field('forum_posts', 'attachment', '1', array('id' => $post->id)); } } } // Clear the mailsink and close the messagesink. // (surely setup should provide us this cleared but...) $this->mailsink->clear(); $this->messagesink->close(); $expect = [ 'author' => (object) [ 'userid' => $user->id, 'messages' => count($posts), ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($user, $posts); // Get the mails. $mails = $this->mailsink->get_messages(); // Start testing the expectations. $expectations = $data['expectations']; // Assert the number is the expected. $this->assertSame(count($expectations), count($mails)); // Start processing mails, first localizing its expectations, then checking them. foreach ($mails as $mail) { // Find the corresponding expectation. $foundexpectation = null; foreach ($expectations as $key => $expectation) { // All expectations must have a subject for matching. if (!isset($expectation['subject'])) { $this->fail('Provider expectation missing mandatory subject'); } if (preg_match('!' . $expectation['subject'] . '!', $mail->subject)) { // If we already had found the expectation, there are non-unique subjects. Fail. if (isset($foundexpectation)) { $this->fail('Multiple expectations found (by subject matching). Please make them unique.'); } $foundexpectation = $expectation; unset($expectations[$key]); } } // Arrived here, we should have found the expectations. $this->assertNotEmpty($foundexpectation, 'Expectation not found for the mail'); // If we have found the expectation and have contents to match, let's do it. if (isset($foundexpectation) and isset($foundexpectation['contents'])) { $mail->body = quoted_printable_decode($mail->body); if (!is_array($foundexpectation['contents'])) { // Accept both string and array. $foundexpectation['contents'] = array($foundexpectation['contents']); } foreach ($foundexpectation['contents'] as $content) { if (strpos($content, '~') !== 0) { $this->assertRegexp('#' . $content . '#m', $mail->body); } else { preg_match('#' . substr($content, 1) . '#m', $mail->body, $matches); $this->assertNotRegexp('#' . substr($content, 1) . '#m', $mail->body); } } } } // Finished, there should not be remaining expectations. $this->assertCount(0, $expectations); } /** * Ensure that posts already mailed are not re-sent. */ public function test_already_mailed() { global $DB; $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $DB->set_field('forum_posts', 'mailed', 1); // No posts shoudl be considered. $this->queue_tasks_and_assert([]); // No notifications should be queued. $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, []); } /** * Ensure that posts marked mailnow are not suspect to the maxeditingtime. */ public function test_mailnow() { global $CFG, $DB; // Update the maxeditingtime to 1 day so that posts won't be sent. $CFG->maxeditingtime = DAYSECS; $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); // Post a discussion to the forum. list($discussion, $postmailednow) = $this->helper_post_to_forum($forum, $author, ['mailnow' => 1]); // Only the mailnow post should be considered. $expect = [ 'author' => (object) [ 'userid' => $author->id, 'messages' => 1, ], 'recipient' => (object) [ 'userid' => $recipient->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); // No notifications should be queued. $this->send_notifications_and_assert($author, [$postmailednow]); $this->send_notifications_and_assert($recipient, [$postmailednow]); } /** * Ensure that if a user has no permission to view a post, then it is not sent. */ public function test_access_coursemodule_hidden() { global $CFG, $DB; $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Create one users enrolled in the course as an editing teacher. list($editor) = $this->helper_create_users($course, 1, 'editingteacher'); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); // Hide the coursemodule. set_coursemodule_visible($forum->cmid, 0); // Only the mailnow post should be considered. $expect = [ 'author' => (object) [ 'userid' => $author->id, 'messages' => 1, ], 'recipient' => (object) [ 'userid' => $recipient->id, 'messages' => 1, ], 'editor' => (object) [ 'userid' => $editor->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); // No notifications should be queued. $this->send_notifications_and_assert($author, [], true); $this->send_notifications_and_assert($recipient, [], true); $this->send_notifications_and_assert($editor, [$post], true); } /** * Ensure that if a user loses permission to view a post after it is queued, that it is not sent. */ public function test_access_coursemodule_hidden_after_queue() { global $CFG, $DB; $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Create one users enrolled in the course as an editing teacher. list($editor) = $this->helper_create_users($course, 1, 'editingteacher'); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); // Only the mailnow post should be considered. $expect = [ 'author' => (object) [ 'userid' => $author->id, 'messages' => 1, ], 'recipient' => (object) [ 'userid' => $recipient->id, 'messages' => 1, ], 'editor' => (object) [ 'userid' => $editor->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); // Hide the coursemodule. set_coursemodule_visible($forum->cmid, 0); // No notifications should be queued for the students. $this->send_notifications_and_assert($author, [], true); $this->send_notifications_and_assert($recipient, [], true); // The editing teacher should still receive the post. $this->send_notifications_and_assert($editor, [$post]); } /** * Ensure that messages are not sent until the timestart. */ public function test_access_before_timestart() { global $CFG, $DB; $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Create one users enrolled in the course as an editing teacher. list($editor) = $this->helper_create_users($course, 1, 'editingteacher'); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); // Update the discussion to have a timestart in the future. $DB->set_field('forum_discussions', 'timestart', time() + DAYSECS); // None should be sent. $this->queue_tasks_and_assert([]); // No notifications should be queued for any user. $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, []); $this->send_notifications_and_assert($editor, []); // Update the discussion to have a timestart in the past. $DB->set_field('forum_discussions', 'timestart', time() - DAYSECS); // Now should be sent to all. $expect = [ 'author' => (object) [ 'userid' => $author->id, 'messages' => 1, ], 'recipient' => (object) [ 'userid' => $recipient->id, 'messages' => 1, ], 'editor' => (object) [ 'userid' => $editor->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); // No notifications should be queued for any user. $this->send_notifications_and_assert($author, [$post]); $this->send_notifications_and_assert($recipient, [$post]); $this->send_notifications_and_assert($editor, [$post]); } /** * Ensure that messages are not sent after the timeend. */ public function test_access_after_timeend() { global $CFG, $DB; $this->resetAfterTest(true); // Create a course, with a forum. $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); // Create two users enrolled in the course as students. list($author, $recipient) = $this->helper_create_users($course, 2); // Create one users enrolled in the course as an editing teacher. list($editor) = $this->helper_create_users($course, 1, 'editingteacher'); // Post a discussion to the forum. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); // Update the discussion to have a timestart in the past. $DB->set_field('forum_discussions', 'timeend', time() - DAYSECS); // None should be sent. $this->queue_tasks_and_assert([]); // No notifications should be queued for any user. $this->send_notifications_and_assert($author, []); $this->send_notifications_and_assert($recipient, []); $this->send_notifications_and_assert($editor, []); // Update the discussion to have a timestart in the past. $DB->set_field('forum_discussions', 'timeend', time() + DAYSECS); // Now should be sent to all. $expect = [ 'author' => (object) [ 'userid' => $author->id, 'messages' => 1, ], 'recipient' => (object) [ 'userid' => $recipient->id, 'messages' => 1, ], 'editor' => (object) [ 'userid' => $editor->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); // No notifications should be queued for any user. $this->send_notifications_and_assert($author, [$post]); $this->send_notifications_and_assert($recipient, [$post]); $this->send_notifications_and_assert($editor, [$post]); } /** * Test notification comes with customdata. */ public function test_notification_customdata() { $this->resetAfterTest(true); $course = $this->getDataGenerator()->create_course(); $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); $forum = $this->getDataGenerator()->create_module('forum', $options); list($author) = $this->helper_create_users($course, 1); list($commenter) = $this->helper_create_users($course, 1); $strre = get_string('re', 'forum'); // New posts should not have Re: in the subject. list($discussion, $post) = $this->helper_post_to_forum($forum, $author); $expect = [ 'author' => (object) [ 'userid' => $author->id, 'messages' => 1, ], 'commenter' => (object) [ 'userid' => $commenter->id, 'messages' => 1, ], ]; $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, [$post]); $this->send_notifications_and_assert($commenter, [$post]); $messages = $this->messagesink->get_messages(); $customdata = json_decode($messages[0]->customdata); $this->assertEquals($forum->id, $customdata->instance); $this->assertEquals($forum->cmid, $customdata->cmid); $this->assertEquals($post->id, $customdata->postid); $this->assertEquals($discussion->id, $customdata->discussionid); $this->assertObjectHasAttribute('notificationiconurl', $customdata); $this->assertObjectHasAttribute('actionbuttons', $customdata); $this->assertCount(1, (array) $customdata->actionbuttons); } }