[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/forum/tests/ -> mail_test.php (source)

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * The forum module mail generation tests.
  19   *
  20   * @package    mod_forum
  21   * @category   external
  22   * @copyright  2013 Andrew Nicols
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  
  30  class mod_forum_mail_testcase extends advanced_testcase {
  31  
  32      protected $helper;
  33  
  34      public function setUp() {
  35          // We must clear the subscription caches. This has to be done both before each test, and after in case of other
  36          // tests using these functions.
  37          \mod_forum\subscriptions::reset_forum_cache();
  38          \mod_forum\subscriptions::reset_discussion_cache();
  39  
  40          global $CFG;
  41          require_once($CFG->dirroot . '/mod/forum/lib.php');
  42  
  43          $helper = new stdClass();
  44  
  45          // Messaging is not compatible with transactions...
  46          $this->preventResetByRollback();
  47  
  48          // Catch all messages.
  49          $helper->messagesink = $this->redirectMessages();
  50          $helper->mailsink = $this->redirectEmails();
  51  
  52          // Confirm that we have an empty message sink so far.
  53          $messages = $helper->messagesink->get_messages();
  54          $this->assertEquals(0, count($messages));
  55  
  56          $messages = $helper->mailsink->get_messages();
  57          $this->assertEquals(0, count($messages));
  58  
  59          // Forcibly reduce the maxeditingtime to a second in the past to
  60          // ensure that messages are sent out.
  61          $CFG->maxeditingtime = -1;
  62  
  63          $this->helper = $helper;
  64      }
  65  
  66      public function tearDown() {
  67          // We must clear the subscription caches. This has to be done both before each test, and after in case of other
  68          // tests using these functions.
  69          \mod_forum\subscriptions::reset_forum_cache();
  70  
  71          $this->helper->messagesink->clear();
  72          $this->helper->messagesink->close();
  73  
  74          $this->helper->mailsink->clear();
  75          $this->helper->mailsink->close();
  76      }
  77  
  78      /**
  79       * Perform message inbound setup for the mod_forum reply handler.
  80       */
  81      protected function helper_spoof_message_inbound_setup() {
  82          global $CFG, $DB;
  83          // Setup the default Inbound Message mailbox settings.
  84          $CFG->messageinbound_domain = 'example.com';
  85          $CFG->messageinbound_enabled = true;
  86  
  87          // Must be no longer than 15 characters.
  88          $CFG->messageinbound_mailbox = 'moodlemoodle123';
  89  
  90          $record = $DB->get_record('messageinbound_handlers', array('classname' => '\mod_forum\message\inbound\reply_handler'));
  91          $record->enabled = true;
  92          $record->id = $DB->update_record('messageinbound_handlers', $record);
  93      }
  94  
  95      /**
  96       * Helper to create the required number of users in the specified
  97       * course.
  98       * Users are enrolled as students.
  99       *
 100       * @param stdClass $course The course object
 101       * @param integer $count The number of users to create
 102       * @return array The users created
 103       */
 104      protected function helper_create_users($course, $count) {
 105          $users = array();
 106  
 107          for ($i = 0; $i < $count; $i++) {
 108              $user = $this->getDataGenerator()->create_user();
 109              $this->getDataGenerator()->enrol_user($user->id, $course->id);
 110              $users[] = $user;
 111          }
 112  
 113          return $users;
 114      }
 115  
 116      /**
 117       * Create a new discussion and post within the specified forum, as the
 118       * specified author.
 119       *
 120       * @param stdClass $forum The forum to post in
 121       * @param stdClass $author The author to post as
 122       * @param array $fields any other fields in discussion (name, message, messageformat, ...)
 123       * @param array An array containing the discussion object, and the post object
 124       */
 125      protected function helper_post_to_forum($forum, $author, $fields = array()) {
 126          global $DB;
 127          $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
 128  
 129          // Create a discussion in the forum, and then add a post to that discussion.
 130          $record = (object)$fields;
 131          $record->course = $forum->course;
 132          $record->userid = $author->id;
 133          $record->forum = $forum->id;
 134          $discussion = $generator->create_discussion($record);
 135  
 136          // Retrieve the post which was created by create_discussion.
 137          $post = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
 138  
 139          return array($discussion, $post);
 140      }
 141  
 142      /**
 143       * Update the post time for the specified post by $factor.
 144       *
 145       * @param stdClass $post The post to update
 146       * @param int $factor The amount to update by
 147       */
 148      protected function helper_update_post_time($post, $factor) {
 149          global $DB;
 150  
 151          // Update the post to have a created in the past.
 152          $DB->set_field('forum_posts', 'created', $post->created + $factor, array('id' => $post->id));
 153      }
 154  
 155      /**
 156       * Update the subscription time for the specified user/discussion by $factor.
 157       *
 158       * @param stdClass $user The user to update
 159       * @param stdClass $discussion The discussion to update for this user
 160       * @param int $factor The amount to update by
 161       */
 162      protected function helper_update_subscription_time($user, $discussion, $factor) {
 163          global $DB;
 164  
 165          $sub = $DB->get_record('forum_discussion_subs', array('userid' => $user->id, 'discussion' => $discussion->id));
 166  
 167          // Update the subscription to have a preference in the past.
 168          $DB->set_field('forum_discussion_subs', 'preference', $sub->preference + $factor, array('id' => $sub->id));
 169      }
 170  
 171      /**
 172       * Create a new post within an existing discussion, as the specified author.
 173       *
 174       * @param stdClass $forum The forum to post in
 175       * @param stdClass $discussion The discussion to post in
 176       * @param stdClass $author The author to post as
 177       * @return stdClass The forum post
 178       */
 179      protected function helper_post_to_discussion($forum, $discussion, $author) {
 180          global $DB;
 181  
 182          $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
 183  
 184          // Add a post to the discussion.
 185          $record = new stdClass();
 186          $record->course = $forum->course;
 187          $strre = get_string('re', 'forum');
 188          $record->subject = $strre . ' ' . $discussion->subject;
 189          $record->userid = $author->id;
 190          $record->forum = $forum->id;
 191          $record->discussion = $discussion->id;
 192          $record->mailnow = 1;
 193  
 194          $post = $generator->create_post($record);
 195  
 196          return $post;
 197      }
 198  
 199      /**
 200       * Run the forum cron, and check that the specified post was sent the
 201       * specified number of times.
 202       *
 203       * @param stdClass $post The forum post object
 204       * @param integer $expected The number of times that the post should have been sent
 205       * @return array An array of the messages caught by the message sink
 206       */
 207      protected function helper_run_cron_check_count($post, $expected) {
 208  
 209          // Clear the sinks before running cron.
 210          $this->helper->messagesink->clear();
 211          $this->helper->mailsink->clear();
 212  
 213          // Cron daily uses mtrace, turn on buffering to silence output.
 214          $this->expectOutputRegex("/{$expected} users were sent post {$post->id}, '{$post->subject}'/");
 215          forum_cron();
 216  
 217          // Now check the results in the message sink.
 218          $messages = $this->helper->messagesink->get_messages();
 219  
 220          // There should be the expected number of messages.
 221          $this->assertEquals($expected, count($messages));
 222  
 223          return $messages;
 224      }
 225  
 226      /**
 227       * Run the forum cron, and check that the specified posts were sent the
 228       * specified number of times.
 229       *
 230       * @param stdClass $post The forum post object
 231       * @param integer $expected The number of times that the post should have been sent
 232       * @return array An array of the messages caught by the message sink
 233       */
 234      protected function helper_run_cron_check_counts($posts, $expected) {
 235  
 236          // Clear the sinks before running cron.
 237          $this->helper->messagesink->clear();
 238          $this->helper->mailsink->clear();
 239  
 240          // Cron daily uses mtrace, turn on buffering to silence output.
 241          foreach ($posts as $post) {
 242              $this->expectOutputRegex("/{$post['count']} users were sent post {$post['id']}, '{$post['subject']}'/");
 243          }
 244          forum_cron();
 245  
 246          // Now check the results in the message sink.
 247          $messages = $this->helper->messagesink->get_messages();
 248  
 249          // There should be the expected number of messages.
 250          $this->assertEquals($expected, count($messages));
 251  
 252          return $messages;
 253      }
 254  
 255      public function test_forced_subscription() {
 256          $this->resetAfterTest(true);
 257  
 258          // Create a course, with a forum.
 259          $course = $this->getDataGenerator()->create_course();
 260  
 261          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
 262          $forum = $this->getDataGenerator()->create_module('forum', $options);
 263  
 264          // Create two users enrolled in the course as students.
 265          list($author, $recipient) = $this->helper_create_users($course, 2);
 266  
 267          // Post a discussion to the forum.
 268          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 269  
 270          // We expect both users to receive this post.
 271          $expected = 2;
 272  
 273          // Run cron and check that the expected number of users received the notification.
 274          $messages = $this->helper_run_cron_check_count($post, $expected);
 275  
 276          $seenauthor = false;
 277          $seenrecipient = false;
 278          foreach ($messages as $message) {
 279              // They should both be from our user.
 280              $this->assertEquals($author->id, $message->useridfrom);
 281  
 282              if ($message->useridto == $author->id) {
 283                  $seenauthor = true;
 284              } else if ($message->useridto = $recipient->id) {
 285                  $seenrecipient = true;
 286              }
 287          }
 288  
 289          // Check we saw messages for both users.
 290          $this->assertTrue($seenauthor);
 291          $this->assertTrue($seenrecipient);
 292      }
 293  
 294      public function test_subscription_disabled() {
 295          global $DB;
 296  
 297          $this->resetAfterTest(true);
 298  
 299          // Create a course, with a forum.
 300          $course = $this->getDataGenerator()->create_course();
 301  
 302          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
 303          $forum = $this->getDataGenerator()->create_module('forum', $options);
 304  
 305          // Create two users enrolled in the course as students.
 306          list($author, $recipient) = $this->helper_create_users($course, 2);
 307  
 308          // Post a discussion to the forum.
 309          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 310  
 311          // We expect both users to receive this post.
 312          $expected = 0;
 313  
 314          // Run cron and check that the expected number of users received the notification.
 315          $messages = $this->helper_run_cron_check_count($post, $expected);
 316  
 317          // A user with the manageactivities capability within the course can subscribe.
 318          $expected = 1;
 319          $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
 320          assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleids['student'], context_course::instance($course->id));
 321          \mod_forum\subscriptions::subscribe_user($recipient->id, $forum);
 322  
 323          $this->assertEquals($expected, $DB->count_records('forum_subscriptions', array(
 324              'userid'        => $recipient->id,
 325              'forum'         => $forum->id,
 326          )));
 327  
 328          // Run cron and check that the expected number of users received the notification.
 329          list($discussion, $post) = $this->helper_post_to_forum($forum, $recipient);
 330          $messages = $this->helper_run_cron_check_count($post, $expected);
 331  
 332          // Unsubscribe the user again.
 333          \mod_forum\subscriptions::unsubscribe_user($recipient->id, $forum);
 334  
 335          $expected = 0;
 336          $this->assertEquals($expected, $DB->count_records('forum_subscriptions', array(
 337              'userid'        => $recipient->id,
 338              'forum'         => $forum->id,
 339          )));
 340  
 341          // Run cron and check that the expected number of users received the notification.
 342          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 343          $messages = $this->helper_run_cron_check_count($post, $expected);
 344  
 345          // Subscribe the user to the discussion.
 346          \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion);
 347          $this->helper_update_subscription_time($recipient, $discussion, -60);
 348  
 349          $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
 350          $this->helper_update_post_time($reply, -30);
 351  
 352          $messages = $this->helper_run_cron_check_count($reply, $expected);
 353      }
 354  
 355      public function test_automatic() {
 356          $this->resetAfterTest(true);
 357  
 358          // Create a course, with a forum.
 359          $course = $this->getDataGenerator()->create_course();
 360  
 361          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
 362          $forum = $this->getDataGenerator()->create_module('forum', $options);
 363  
 364          // Create two users enrolled in the course as students.
 365          list($author, $recipient) = $this->helper_create_users($course, 2);
 366  
 367          // Post a discussion to the forum.
 368          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 369  
 370          // We expect both users to receive this post.
 371          $expected = 2;
 372  
 373          // Run cron and check that the expected number of users received the notification.
 374          $messages = $this->helper_run_cron_check_count($post, $expected);
 375  
 376          $seenauthor = false;
 377          $seenrecipient = false;
 378          foreach ($messages as $message) {
 379              // They should both be from our user.
 380              $this->assertEquals($author->id, $message->useridfrom);
 381  
 382              if ($message->useridto == $author->id) {
 383                  $seenauthor = true;
 384              } else if ($message->useridto = $recipient->id) {
 385                  $seenrecipient = true;
 386              }
 387          }
 388  
 389          // Check we saw messages for both users.
 390          $this->assertTrue($seenauthor);
 391          $this->assertTrue($seenrecipient);
 392      }
 393  
 394      public function test_optional() {
 395          $this->resetAfterTest(true);
 396  
 397          // Create a course, with a forum.
 398          $course = $this->getDataGenerator()->create_course();
 399  
 400          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 401          $forum = $this->getDataGenerator()->create_module('forum', $options);
 402  
 403          // Create two users enrolled in the course as students.
 404          list($author, $recipient) = $this->helper_create_users($course, 2);
 405  
 406          // Post a discussion to the forum.
 407          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 408  
 409          // We expect both users to receive this post.
 410          $expected = 0;
 411  
 412          // Run cron and check that the expected number of users received the notification.
 413          $messages = $this->helper_run_cron_check_count($post, $expected);
 414      }
 415  
 416      public function test_automatic_with_unsubscribed_user() {
 417          $this->resetAfterTest(true);
 418  
 419          // Create a course, with a forum.
 420          $course = $this->getDataGenerator()->create_course();
 421  
 422          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
 423          $forum = $this->getDataGenerator()->create_module('forum', $options);
 424  
 425          // Create two users enrolled in the course as students.
 426          list($author, $recipient) = $this->helper_create_users($course, 2);
 427  
 428          // Unsubscribe the 'author' user from the forum.
 429          \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
 430  
 431          // Post a discussion to the forum.
 432          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 433  
 434          // We expect only one user to receive this post.
 435          $expected = 1;
 436  
 437          // Run cron and check that the expected number of users received the notification.
 438          $messages = $this->helper_run_cron_check_count($post, $expected);
 439  
 440          $seenauthor = false;
 441          $seenrecipient = false;
 442          foreach ($messages as $message) {
 443              // They should both be from our user.
 444              $this->assertEquals($author->id, $message->useridfrom);
 445  
 446              if ($message->useridto == $author->id) {
 447                  $seenauthor = true;
 448              } else if ($message->useridto = $recipient->id) {
 449                  $seenrecipient = true;
 450              }
 451          }
 452  
 453          // Check we only saw one user.
 454          $this->assertFalse($seenauthor);
 455          $this->assertTrue($seenrecipient);
 456      }
 457  
 458      public function test_optional_with_subscribed_user() {
 459          $this->resetAfterTest(true);
 460  
 461          // Create a course, with a forum.
 462          $course = $this->getDataGenerator()->create_course();
 463  
 464          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 465          $forum = $this->getDataGenerator()->create_module('forum', $options);
 466  
 467          // Create two users enrolled in the course as students.
 468          list($author, $recipient) = $this->helper_create_users($course, 2);
 469  
 470          // Subscribe the 'recipient' user from the forum.
 471          \mod_forum\subscriptions::subscribe_user($recipient->id, $forum);
 472  
 473          // Post a discussion to the forum.
 474          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 475  
 476          // We expect only one user to receive this post.
 477          $expected = 1;
 478  
 479          // Run cron and check that the expected number of users received the notification.
 480          $messages = $this->helper_run_cron_check_count($post, $expected);
 481  
 482          $seenauthor = false;
 483          $seenrecipient = false;
 484          foreach ($messages as $message) {
 485              // They should both be from our user.
 486              $this->assertEquals($author->id, $message->useridfrom);
 487  
 488              if ($message->useridto == $author->id) {
 489                  $seenauthor = true;
 490              } else if ($message->useridto = $recipient->id) {
 491                  $seenrecipient = true;
 492              }
 493          }
 494  
 495          // Check we only saw one user.
 496          $this->assertFalse($seenauthor);
 497          $this->assertTrue($seenrecipient);
 498      }
 499  
 500      public function test_automatic_with_unsubscribed_discussion() {
 501          $this->resetAfterTest(true);
 502  
 503          // Create a course, with a forum.
 504          $course = $this->getDataGenerator()->create_course();
 505  
 506          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
 507          $forum = $this->getDataGenerator()->create_module('forum', $options);
 508  
 509          // Create two users enrolled in the course as students.
 510          list($author, $recipient) = $this->helper_create_users($course, 2);
 511  
 512          // Post a discussion to the forum.
 513          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 514  
 515          // Unsubscribe the 'author' user from the discussion.
 516          \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
 517  
 518          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 519          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($recipient->id, $forum, $discussion->id));
 520  
 521          // We expect only one user to receive this post.
 522          $expected = 1;
 523  
 524          // Run cron and check that the expected number of users received the notification.
 525          $messages = $this->helper_run_cron_check_count($post, $expected);
 526  
 527          $seenauthor = false;
 528          $seenrecipient = false;
 529          foreach ($messages as $message) {
 530              // They should both be from our user.
 531              $this->assertEquals($author->id, $message->useridfrom);
 532  
 533              if ($message->useridto == $author->id) {
 534                  $seenauthor = true;
 535              } else if ($message->useridto = $recipient->id) {
 536                  $seenrecipient = true;
 537              }
 538          }
 539  
 540          // Check we only saw one user.
 541          $this->assertFalse($seenauthor);
 542          $this->assertTrue($seenrecipient);
 543      }
 544  
 545      public function test_optional_with_subscribed_discussion() {
 546          $this->resetAfterTest(true);
 547  
 548          // Create a course, with a forum.
 549          $course = $this->getDataGenerator()->create_course();
 550  
 551          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 552          $forum = $this->getDataGenerator()->create_module('forum', $options);
 553  
 554          // Create two users enrolled in the course as students.
 555          list($author, $recipient) = $this->helper_create_users($course, 2);
 556  
 557          // Post a discussion to the forum.
 558          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 559          $this->helper_update_post_time($post, -90);
 560  
 561          // Subscribe the 'recipient' user to the discussion.
 562          \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion);
 563          $this->helper_update_subscription_time($recipient, $discussion, -60);
 564  
 565          // Initially we don't expect any user to receive this post as you cannot subscribe to a discussion until after
 566          // you have read it.
 567          $expected = 0;
 568  
 569          // Run cron and check that the expected number of users received the notification.
 570          $messages = $this->helper_run_cron_check_count($post, $expected);
 571  
 572          // Have a user reply to the discussion.
 573          $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
 574          $this->helper_update_post_time($reply, -30);
 575  
 576          // We expect only one user to receive this post.
 577          $expected = 1;
 578  
 579          // Run cron and check that the expected number of users received the notification.
 580          $messages = $this->helper_run_cron_check_count($reply, $expected);
 581  
 582          $seenauthor = false;
 583          $seenrecipient = false;
 584          foreach ($messages as $message) {
 585              // They should both be from our user.
 586              $this->assertEquals($author->id, $message->useridfrom);
 587  
 588              if ($message->useridto == $author->id) {
 589                  $seenauthor = true;
 590              } else if ($message->useridto = $recipient->id) {
 591                  $seenrecipient = true;
 592              }
 593          }
 594  
 595          // Check we only saw one user.
 596          $this->assertFalse($seenauthor);
 597          $this->assertTrue($seenrecipient);
 598      }
 599  
 600      public function test_automatic_with_subscribed_discussion_in_unsubscribed_forum() {
 601          $this->resetAfterTest(true);
 602  
 603          // Create a course, with a forum.
 604          $course = $this->getDataGenerator()->create_course();
 605  
 606          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
 607          $forum = $this->getDataGenerator()->create_module('forum', $options);
 608  
 609          // Create two users enrolled in the course as students.
 610          list($author, $recipient) = $this->helper_create_users($course, 2);
 611  
 612          // Post a discussion to the forum.
 613          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 614          $this->helper_update_post_time($post, -90);
 615  
 616          // Unsubscribe the 'author' user from the forum.
 617          \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
 618  
 619          // Then re-subscribe them to the discussion.
 620          \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
 621          $this->helper_update_subscription_time($author, $discussion, -60);
 622  
 623          // We expect just the user subscribed to the forum to receive this post at the moment as the discussion
 624          // subscription time is after the post time.
 625          $expected = 1;
 626  
 627          // Run cron and check that the expected number of users received the notification.
 628          $messages = $this->helper_run_cron_check_count($post, $expected);
 629  
 630          $seenauthor = false;
 631          $seenrecipient = false;
 632          foreach ($messages as $message) {
 633              // They should both be from our user.
 634              $this->assertEquals($author->id, $message->useridfrom);
 635  
 636              if ($message->useridto == $author->id) {
 637                  $seenauthor = true;
 638              } else if ($message->useridto = $recipient->id) {
 639                  $seenrecipient = true;
 640              }
 641          }
 642  
 643          // Check we only saw one user.
 644          $this->assertFalse($seenauthor);
 645          $this->assertTrue($seenrecipient);
 646  
 647          // Now post a reply to the original post.
 648          $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
 649          $this->helper_update_post_time($reply, -30);
 650  
 651          // We expect two users to receive this post.
 652          $expected = 2;
 653  
 654          // Run cron and check that the expected number of users received the notification.
 655          $messages = $this->helper_run_cron_check_count($reply, $expected);
 656  
 657          $seenauthor = false;
 658          $seenrecipient = false;
 659          foreach ($messages as $message) {
 660              // They should both be from our user.
 661              $this->assertEquals($author->id, $message->useridfrom);
 662  
 663              if ($message->useridto == $author->id) {
 664                  $seenauthor = true;
 665              } else if ($message->useridto = $recipient->id) {
 666                  $seenrecipient = true;
 667              }
 668          }
 669  
 670          // Check we saw both users.
 671          $this->assertTrue($seenauthor);
 672          $this->assertTrue($seenrecipient);
 673      }
 674  
 675      public function test_optional_with_unsubscribed_discussion_in_subscribed_forum() {
 676          $this->resetAfterTest(true);
 677  
 678          // Create a course, with a forum.
 679          $course = $this->getDataGenerator()->create_course();
 680  
 681          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 682          $forum = $this->getDataGenerator()->create_module('forum', $options);
 683  
 684          // Create two users enrolled in the course as students.
 685          list($author, $recipient) = $this->helper_create_users($course, 2);
 686  
 687          // Post a discussion to the forum.
 688          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 689  
 690          // Unsubscribe the 'recipient' user from the discussion.
 691          \mod_forum\subscriptions::subscribe_user($recipient->id, $forum);
 692  
 693          // Then unsubscribe them from the discussion.
 694          \mod_forum\subscriptions::unsubscribe_user_from_discussion($recipient->id, $discussion);
 695  
 696          // We don't expect any users to receive this post.
 697          $expected = 0;
 698  
 699          // Run cron and check that the expected number of users received the notification.
 700          $messages = $this->helper_run_cron_check_count($post, $expected);
 701      }
 702  
 703      /**
 704       * Test that a user unsubscribed from a forum who has subscribed to a discussion, only receives posts made after
 705       * they subscribed to the discussion.
 706       */
 707      public function test_forum_discussion_subscription_forum_unsubscribed_discussion_subscribed_after_post() {
 708          $this->resetAfterTest(true);
 709  
 710          // Create a course, with a forum.
 711          $course = $this->getDataGenerator()->create_course();
 712  
 713          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 714          $forum = $this->getDataGenerator()->create_module('forum', $options);
 715  
 716          $expectedmessages = array();
 717  
 718          // Create a user enrolled in the course as a student.
 719          list($author) = $this->helper_create_users($course, 1);
 720  
 721          // Post a discussion to the forum.
 722          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 723          $this->helper_update_post_time($post, -90);
 724  
 725          $expectedmessages[] = array(
 726              'id' => $post->id,
 727              'subject' => $post->subject,
 728              'count' => 0,
 729          );
 730  
 731          // Then subscribe the user to the discussion.
 732          $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));
 733          $this->helper_update_subscription_time($author, $discussion, -60);
 734  
 735          // Then post a reply to the first discussion.
 736          $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
 737          $this->helper_update_post_time($reply, -30);
 738  
 739          $expectedmessages[] = array(
 740              'id' => $reply->id,
 741              'subject' => $reply->subject,
 742              'count' => 1,
 743          );
 744  
 745          $expectedcount = 1;
 746  
 747          // Run cron and check that the expected number of users received the notification.
 748          $messages = $this->helper_run_cron_check_counts($expectedmessages, $expectedcount);
 749      }
 750  
 751      public function test_forum_message_inbound_multiple_posts() {
 752          $this->resetAfterTest(true);
 753  
 754          // Create a course, with a forum.
 755          $course = $this->getDataGenerator()->create_course();
 756          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
 757          $forum = $this->getDataGenerator()->create_module('forum', $options);
 758  
 759          // Create a user enrolled in the course as a student.
 760          list($author) = $this->helper_create_users($course, 1);
 761  
 762          $expectedmessages = array();
 763  
 764          // Post a discussion to the forum.
 765          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 766          $this->helper_update_post_time($post, -90);
 767  
 768          $expectedmessages[] = array(
 769              'id' => $post->id,
 770              'subject' => $post->subject,
 771              'count' => 0,
 772          );
 773  
 774          // Then post a reply to the first discussion.
 775          $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
 776          $this->helper_update_post_time($reply, -60);
 777  
 778          $expectedmessages[] = array(
 779              'id' => $reply->id,
 780              'subject' => $reply->subject,
 781              'count' => 1,
 782          );
 783  
 784          $expectedcount = 2;
 785  
 786          // Ensure that messageinbound is enabled and configured for the forum handler.
 787          $this->helper_spoof_message_inbound_setup();
 788  
 789          $author->emailstop = '0';
 790          set_user_preference('message_provider_mod_forum_posts_loggedoff', 'email', $author);
 791          set_user_preference('message_provider_mod_forum_posts_loggedin', 'email', $author);
 792  
 793          // Run cron and check that the expected number of users received the notification.
 794          // Clear the mailsink, and close the messagesink.
 795          $this->helper->mailsink->clear();
 796          $this->helper->messagesink->close();
 797  
 798          // Cron daily uses mtrace, turn on buffering to silence output.
 799          foreach ($expectedmessages as $post) {
 800              $this->expectOutputRegex("/{$post['count']} users were sent post {$post['id']}, '{$post['subject']}'/");
 801          }
 802  
 803          forum_cron();
 804          $messages = $this->helper->mailsink->get_messages();
 805  
 806          // There should be the expected number of messages.
 807          $this->assertEquals($expectedcount, count($messages));
 808  
 809          foreach ($messages as $message) {
 810              $this->assertRegExp('/Reply-To: moodlemoodle123\+[^@]*@example.com/', $message->header);
 811          }
 812      }
 813  
 814      public function test_long_subject() {
 815          $this->resetAfterTest(true);
 816  
 817          // Create a course, with a forum.
 818          $course = $this->getDataGenerator()->create_course();
 819  
 820          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
 821          $forum = $this->getDataGenerator()->create_module('forum', $options);
 822  
 823          // Create a user enrolled in the course as student.
 824          list($author) = $this->helper_create_users($course, 1);
 825  
 826          // Post a discussion to the forum.
 827          $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.';
 828          $a = (object)array('courseshortname' => $course->shortname, 'forumname' => $forum->name, 'subject' => $subject);
 829          $expectedsubject = get_string('postmailsubject', 'forum', $a);
 830          list($discussion, $post) = $this->helper_post_to_forum($forum, $author, array('name' => $subject));
 831  
 832          // Run cron and check that the expected number of users received the notification.
 833          $messages = $this->helper_run_cron_check_count($post, 1);
 834          $message = reset($messages);
 835          $this->assertEquals($author->id, $message->useridfrom);
 836          $this->assertEquals($expectedsubject, $message->subject);
 837      }
 838  
 839      /**
 840       * Test inital email and reply email subjects
 841       */
 842      public function test_subjects() {
 843          $this->resetAfterTest(true);
 844  
 845          $course = $this->getDataGenerator()->create_course();
 846  
 847          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
 848          $forum = $this->getDataGenerator()->create_module('forum', $options);
 849  
 850          list($author) = $this->helper_create_users($course, 1);
 851          list($commenter) = $this->helper_create_users($course, 1);
 852  
 853          $strre = get_string('re', 'forum');
 854  
 855          // New posts should not have Re: in the subject.
 856          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 857          $messages = $this->helper_run_cron_check_count($post, 2);
 858          $this->assertNotContains($strre, $messages[0]->subject);
 859  
 860          // Replies should have Re: in the subject.
 861          $reply = $this->helper_post_to_discussion($forum, $discussion, $commenter);
 862          $messages = $this->helper_run_cron_check_count($reply, 2);
 863          $this->assertContains($strre, $messages[0]->subject);
 864      }
 865  
 866      /**
 867       * dataProvider for test_forum_post_email_templates().
 868       */
 869      public function forum_post_email_templates_provider() {
 870          // Base information, we'll build variations based on it.
 871          $base = array(
 872              'user' => array('firstname' => 'Love', 'lastname' => 'Moodle', 'mailformat' => 0, 'maildigest' => 0),
 873              'course' => array('shortname' => '101', 'fullname' => 'Moodle 101'),
 874              'forums' => array(
 875                  array(
 876                      'name' => 'Moodle Forum',
 877                      'forumposts' => array(
 878                          array(
 879                              'name' => 'Hello Moodle',
 880                              'message' => 'Welcome to Moodle',
 881                              'messageformat' => FORMAT_MOODLE,
 882                              'attachments' => array(
 883                                  array(
 884                                      'filename' => 'example.txt',
 885                                      'filecontents' => 'Basic information about the course'
 886                                  ),
 887                              ),
 888                          ),
 889                      ),
 890                  ),
 891              ),
 892              'expectations' => array(
 893                  array(
 894                      'subject' => '.*101.*Hello',
 895                      'contents' => array(
 896                          '~{$a',
 897                          '~&(amp|lt|gt|quot|\#039);(?!course)',
 898                          'Attachment example.txt:\n' .
 899                              'http://www.example.com/moodle/pluginfile.php/\d*/mod_forum/attachment/\d*/example.txt\n',
 900                          'Hello Moodle', 'Moodle Forum', 'Welcome.*Moodle', 'Love Moodle', '1\d1'
 901                      ),
 902                  ),
 903              ),
 904          );
 905  
 906          // Build the text cases.
 907          $textcases = array('Text mail without ampersands, quotes or lt/gt' => array('data' => $base));
 908  
 909          // Single and double quotes everywhere.
 910          $newcase = $base;
 911          $newcase['user']['lastname'] = 'Moodle\'"';
 912          $newcase['course']['shortname'] = '101\'"';
 913          $newcase['forums'][0]['name'] = 'Moodle Forum\'"';
 914          $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle\'"';
 915          $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle\'"';
 916          $newcase['expectations'][0]['contents'] = array(
 917              'Attachment example.txt:', '~{\$a', '~&amp;(quot|\#039);', 'Love Moodle\'', '101\'', 'Moodle Forum\'"',
 918              'Hello Moodle\'"', 'Welcome to Moodle\'"');
 919          $textcases['Text mail with quotes everywhere'] = array('data' => $newcase);
 920  
 921          // Lt and gt everywhere. This case is completely borked because format_string()
 922          // strips tags with $CFG->formatstringstriptags and also escapes < and > (correct
 923          // for web presentation but not for text email). See MDL-19829.
 924          $newcase = $base;
 925          $newcase['user']['lastname'] = 'Moodle>';
 926          $newcase['course']['shortname'] = '101>';
 927          $newcase['forums'][0]['name'] = 'Moodle Forum>';
 928          $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle>';
 929          $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle>';
 930          $newcase['expectations'][0]['contents'] = array(
 931              'Attachment example.txt:', '~{\$a', '~&amp;gt;', 'Love Moodle>', '101>', 'Moodle Forum>',
 932              'Hello Moodle>', 'Welcome to Moodle>');
 933          $textcases['Text mail with gt and lt everywhere'] = array('data' => $newcase);
 934  
 935          // Ampersands everywhere. This case is completely borked because format_string()
 936          // escapes ampersands (correct for web presentation but not for text email). See MDL-19829.
 937          $newcase = $base;
 938          $newcase['user']['lastname'] = 'Moodle&';
 939          $newcase['course']['shortname'] = '101&';
 940          $newcase['forums'][0]['name'] = 'Moodle Forum&';
 941          $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle&';
 942          $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle&';
 943          $newcase['expectations'][0]['contents'] = array(
 944              'Attachment example.txt:', '~{\$a', '~&amp;amp;', 'Love Moodle&', '101&', 'Moodle Forum&',
 945              'Hello Moodle&', 'Welcome to Moodle&');
 946          $textcases['Text mail with ampersands everywhere'] = array('data' => $newcase);
 947  
 948          // Text+image message i.e. @@PLUGINFILE@@ token handling.
 949          $newcase = $base;
 950          $newcase['forums'][0]['forumposts'][0]['name'] = 'Text and image';
 951          $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle, '
 952              .'@@PLUGINFILE@@/Screen%20Shot%202016-03-22%20at%205.54.36%20AM%20%281%29.png !';
 953          $newcase['expectations'][0]['subject'] = '.*101.*Text and image';
 954          $newcase['expectations'][0]['contents'] = array(
 955              '~{$a',
 956              '~&(amp|lt|gt|quot|\#039);(?!course)',
 957              'Attachment example.txt:\n' .
 958              'http://www.example.com/moodle/pluginfile.php/\d*/mod_forum/attachment/\d*/example.txt\n',
 959              'Text and image', 'Moodle Forum',
 960              'Welcome to Moodle, *\n.*'
 961                  .'http://www.example.com/moodle/pluginfile.php/\d+/mod_forum/post/\d+/'
 962                  .'Screen%20Shot%202016-03-22%20at%205\.54\.36%20AM%20%281%29\.png *\n.*!',
 963              'Love Moodle', '1\d1');
 964          $textcases['Text mail with text+image message i.e. @@PLUGINFILE@@ token handling'] = array('data' => $newcase);
 965  
 966          // Now the html cases.
 967          $htmlcases = array();
 968  
 969          // New base for html cases, no quotes, lts, gts or ampersands.
 970          $htmlbase = $base;
 971          $htmlbase['user']['mailformat'] = 1;
 972          $htmlbase['expectations'][0]['contents'] = array(
 973              '~{\$a',
 974              '~&(amp|lt|gt|quot|\#039);(?!course)',
 975              '<div class="attachments">( *\n *)?<a href',
 976              '<div class="subject">\n.*Hello Moodle', '>Moodle Forum', '>Welcome.*Moodle', '>Love Moodle', '>1\d1');
 977          $htmlcases['HTML mail without ampersands, quotes or lt/gt'] = array('data' => $htmlbase);
 978  
 979          // Single and double quotes, lt and gt, ampersands everywhere.
 980          $newcase = $htmlbase;
 981          $newcase['user']['lastname'] = 'Moodle\'">&';
 982          $newcase['course']['shortname'] = '101\'">&';
 983          $newcase['forums'][0]['name'] = 'Moodle Forum\'">&';
 984          $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle\'">&';
 985          $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle\'">&';
 986          $newcase['expectations'][0]['contents'] = array(
 987              '~{\$a',
 988              '~&amp;(amp|lt|gt|quot|\#039);',
 989              '<div class="attachments">( *\n *)?<a href',
 990              '<div class="subject">\n.*Hello Moodle\'"&gt;&amp;', '>Moodle Forum\'"&gt;&amp;',
 991              '>Welcome.*Moodle\'"&gt;&amp;', '>Love Moodle&\#039;&quot;&gt;&amp;', '>101\'"&gt;&amp');
 992          $htmlcases['HTML mail with quotes, gt, lt and ampersand  everywhere'] = array('data' => $newcase);
 993  
 994          // Text+image message i.e. @@PLUGINFILE@@ token handling.
 995          $newcase = $htmlbase;
 996          $newcase['forums'][0]['forumposts'][0]['name'] = 'HTML text and image';
 997          $newcase['forums'][0]['forumposts'][0]['message'] = '<p>Welcome to Moodle, '
 998              .'<img src="@@PLUGINFILE@@/Screen%20Shot%202016-03-22%20at%205.54.36%20AM%20%281%29.png"'
 999              .' alt="" width="200" height="393" class="img-responsive" />!</p>';
1000          $newcase['expectations'][0]['subject'] = '.*101.*HTML text and image';
1001          $newcase['expectations'][0]['contents'] = array(
1002              '~{\$a',
1003              '~&(amp|lt|gt|quot|\#039);(?!course)',
1004              '<div class="attachments">( *\n *)?<a href',
1005              '<div class="subject">\n.*HTML text and image', '>Moodle Forum',
1006              '<p>Welcome to Moodle, '
1007                  .'<img src="http://www.example.com/moodle/pluginfile.php/\d+/mod_forum/post/\d+/'
1008                  .'Screen%20Shot%202016-03-22%20at%205\.54\.36%20AM%20%281%29\.png"'
1009                  .' alt="" width="200" height="393" class="img-responsive" />!</p>',
1010              '>Love Moodle', '>1\d1');
1011          $htmlcases['HTML mail with text+image message i.e. @@PLUGINFILE@@ token handling'] = array('data' => $newcase);
1012  
1013          return $textcases + $htmlcases;
1014      }
1015  
1016      /**
1017       * Verify forum emails body using templates to generate the expected results.
1018       *
1019       * @dataProvider forum_post_email_templates_provider
1020       * @param array $data provider samples.
1021       */
1022      public function test_forum_post_email_templates($data) {
1023          global $DB;
1024  
1025          $this->resetAfterTest();
1026  
1027          // Create the course, with the specified options.
1028          $options = array();
1029          foreach ($data['course'] as $option => $value) {
1030              $options[$option] = $value;
1031          }
1032          $course = $this->getDataGenerator()->create_course($options);
1033  
1034          // Create the user, with the specified options and enrol in the course.
1035          $options = array();
1036          foreach ($data['user'] as $option => $value) {
1037              $options[$option] = $value;
1038          }
1039          $user = $this->getDataGenerator()->create_user($options);
1040          $this->getDataGenerator()->enrol_user($user->id, $course->id);
1041  
1042          // Create forums, always force susbscribed (for easy), with the specified options.
1043          $posts = array();
1044          foreach ($data['forums'] as $dataforum) {
1045              $forumposts = isset($dataforum['forumposts']) ? $dataforum['forumposts'] : array();
1046              unset($dataforum['forumposts']);
1047              $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
1048              foreach ($dataforum as $option => $value) {
1049                  $options[$option] = $value;
1050              }
1051              $forum = $this->getDataGenerator()->create_module('forum', $options);
1052  
1053              // Create posts, always for immediate delivery (for easy), with the specified options.
1054              foreach ($forumposts as $forumpost) {
1055                  $attachments = isset($forumpost['attachments']) ? $forumpost['attachments'] : array();
1056                  unset($forumpost['attachments']);
1057                  $postoptions = array('course' => $course->id, 'forum' => $forum->id, 'userid' => $user->id,
1058                      'mailnow' => 1, 'attachment' => !empty($attachments));
1059                  foreach ($forumpost as $option => $value) {
1060                      $postoptions[$option] = $value;
1061                  }
1062                  list($discussion, $post) = $this->helper_post_to_forum($forum, $user, $postoptions);
1063                  $posts[$post->subject] = $post; // Need this to verify cron output.
1064  
1065                  // Add the attachments to the post.
1066                  if ($attachments) {
1067                      $fs = get_file_storage();
1068                      foreach ($attachments as $attachment) {
1069                          $filerecord = array(
1070                              'contextid' => context_module::instance($forum->cmid)->id,
1071                              'component' => 'mod_forum',
1072                              'filearea'  => 'attachment',
1073                              'itemid'    => $post->id,
1074                              'filepath'  => '/',
1075                              'filename'  => $attachment['filename']
1076                          );
1077                          $fs->create_file_from_string($filerecord, $attachment['filecontents']);
1078                      }
1079                      $DB->set_field('forum_posts', 'attachment', '1', array('id' => $post->id));
1080                  }
1081              }
1082          }
1083  
1084          // Clear the mailsink and close the messagesink.
1085          // (surely setup should provide us this cleared but...)
1086          $this->helper->mailsink->clear();
1087          $this->helper->messagesink->close();
1088  
1089          // Capture and silence cron output, verifying contents.
1090          foreach ($posts as $post) {
1091              $this->expectOutputRegex("/1 users were sent post {$post->id}, '{$post->subject}'/");
1092          }
1093          forum_cron(); // It's really annoying that we have to run cron to test this.
1094  
1095          // Get the mails.
1096          $mails = $this->helper->mailsink->get_messages();
1097  
1098          // Start testing the expectations.
1099          $expectations = $data['expectations'];
1100  
1101          // Assert the number is the expected.
1102          $this->assertSame(count($expectations), count($mails));
1103  
1104          // Start processing mails, first localizing its expectations, then checking them.
1105          foreach ($mails as $mail) {
1106              // Find the corresponding expectation.
1107              $foundexpectation = null;
1108              foreach ($expectations as $key => $expectation) {
1109                  // All expectations must have a subject for matching.
1110                  if (!isset($expectation['subject'])) {
1111                      $this->fail('Provider expectation missing mandatory subject');
1112                  }
1113                  if (preg_match('!' . $expectation['subject'] . '!', $mail->subject)) {
1114                      // If we already had found the expectation, there are non-unique subjects. Fail.
1115                      if (isset($foundexpectation)) {
1116                          $this->fail('Multiple expectations found (by subject matching). Please make them unique.');
1117                      }
1118                      $foundexpectation = $expectation;
1119                      unset($expectations[$key]);
1120                  }
1121              }
1122              // Arrived here, we should have found the expectations.
1123              $this->assertNotEmpty($foundexpectation, 'Expectation not found for the mail');
1124  
1125              // If we have found the expectation and have contents to match, let's do it.
1126              if (isset($foundexpectation) and isset($foundexpectation['contents'])) {
1127                  $mail->body = quoted_printable_decode($mail->body);
1128                  if (!is_array($foundexpectation['contents'])) { // Accept both string and array.
1129                      $foundexpectation['contents'] = array($foundexpectation['contents']);
1130                  }
1131                  foreach ($foundexpectation['contents'] as $content) {
1132                      if (strpos($content, '~') !== 0) {
1133                          $this->assertRegexp('#' . $content . '#m', $mail->body);
1134                      } else {
1135                          preg_match('#' . substr($content, 1) . '#m', $mail->body, $matches);
1136                          $this->assertNotRegexp('#' . substr($content, 1) . '#m', $mail->body);
1137                      }
1138                  }
1139              }
1140          }
1141          // Finished, there should not be remaining expectations.
1142          $this->assertCount(0, $expectations);
1143      }
1144  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1