Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   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 post exporter tests.
  19   *
  20   * @package    mod_forum
  21   * @copyright  2019 Ryan Wyllie <ryan@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  use \mod_forum\local\entities\discussion as discussion_entity;
  28  use \mod_forum\local\entities\post as post_entity;
  29  use \mod_forum\local\exporters\post as post_exporter;
  30  use \mod_forum\local\managers\capability as capability_manager;
  31  
  32  global $CFG;
  33  require_once (__DIR__ . '/generator_trait.php');
  34  require_once($CFG->dirroot . '/rating/lib.php');
  35  
  36  /**
  37   * The post exporter tests.
  38   *
  39   * @package    mod_forum
  40   * @copyright  2019 Ryan Wyllie <ryan@moodle.com>
  41   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  42   */
  43  class mod_forum_exporters_post_testcase extends advanced_testcase {
  44      // Make use of the test generator trait.
  45      use mod_forum_tests_generator_trait;
  46  
  47      /**
  48       * Test the export function returns expected values.
  49       *
  50       * @dataProvider export_post_provider
  51       * @param bool $istimed True if this is a timed post
  52       * @param int $addtime Seconds to be added to the current time
  53       */
  54      public function test_export_post($istimed = false, $addtime = 0) {
  55          global $CFG, $PAGE;
  56          $this->resetAfterTest();
  57  
  58          $CFG->enableportfolios = true;
  59          $filestorage = get_file_storage();
  60          $renderer = $PAGE->get_renderer('core');
  61          $datagenerator = $this->getDataGenerator();
  62          $forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
  63          $user = $datagenerator->create_user();
  64          $course = $datagenerator->create_course();
  65          $forum = $datagenerator->create_module('forum', ['course' => $course->id]);
  66          $coursemodule = get_coursemodule_from_instance('forum', $forum->id);
  67          $context = context_module::instance($coursemodule->id);
  68          $now = time();
  69  
  70          $forumgenparams = [
  71              'course' => $forum->course,
  72              'userid' => $user->id,
  73              'forum' => $forum->id,
  74          ];
  75          if ($istimed) {
  76              $forumgenparams['timestart'] = $now + $addtime;
  77          }
  78          $discussion = $forumgenerator->create_discussion((object) $forumgenparams);
  79  
  80          $post = $forumgenerator->create_post((object) [
  81              'discussion' => $discussion->id,
  82              'parent' => 0,
  83              'userid' => $user->id,
  84              'created' => $now,
  85              'modified' => $now,
  86              'subject' => 'This is the subject',
  87              'message' => 'This is the message',
  88              'messagetrust' => 1,
  89              'attachment' => 0,
  90              'totalscore' => 0,
  91              'mailnow' => 1,
  92              'deleted' => 0
  93          ]);
  94  
  95          \core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id, $context, ['foo', 'bar']);
  96          $tags = \core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id);
  97          $attachment = $filestorage->create_file_from_string(
  98              [
  99                  'contextid' => $context->id,
 100                  'component' => 'mod_forum',
 101                  'filearea'  => 'attachment',
 102                  'itemid'    => $post->id,
 103                  'filepath'  => '/',
 104                  'filename'  => 'example1.jpg',
 105              ],
 106              'image contents'
 107          );
 108  
 109          $canview = true;
 110          $canedit = true;
 111          $candelete = true;
 112          $cansplit = true;
 113          $canreply = true;
 114          $canexport = true;
 115          $cancontrolreadstatus = true;
 116          $canreplyprivately = true;
 117          $canenrol = true;
 118          $capabilitymanager = new test_capability_manager(
 119              $canview,
 120              $canedit,
 121              $candelete,
 122              $cansplit,
 123              $canreply,
 124              $canexport,
 125              $cancontrolreadstatus,
 126              $canreplyprivately,
 127              $canenrol
 128          );
 129          $managerfactory = \mod_forum\local\container::get_manager_factory();
 130          $entityfactory = \mod_forum\local\container::get_entity_factory();
 131          $forum = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
 132          $discussion = $entityfactory->get_discussion_from_stdclass($discussion);
 133          $post = $entityfactory->get_post_from_stdclass($post);
 134          $author = $entityfactory->get_author_from_stdclass($user);
 135          $authorcontext = context_user::instance($author->get_id());
 136  
 137          $exporter = new post_exporter($post, [
 138              'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
 139              'capabilitymanager' => $capabilitymanager,
 140              'readreceiptcollection' => null,
 141              'urlfactory' => \mod_forum\local\container::get_url_factory(),
 142              'forum' => $forum,
 143              'discussion' => $discussion,
 144              'author' => $author,
 145              'authorcontextid' => $authorcontext->id,
 146              'user' => $user,
 147              'context' => $context,
 148              'authorgroups' => [],
 149              'attachments' => [$attachment],
 150              'tags' => $tags,
 151              'rating' => null,
 152              'includehtml' => true
 153          ]);
 154  
 155          $exportedpost = $exporter->export($renderer);
 156  
 157          $this->assertEquals('This is the subject', $exportedpost->subject);
 158          $this->assertEquals('This is the message', $exportedpost->message);
 159          $this->assertEquals($user->id, $exportedpost->author->id);
 160          $this->assertEquals($discussion->get_id(), $exportedpost->discussionid);
 161          $this->assertEquals(false, $exportedpost->hasparent);
 162          $this->assertEquals(null, $exportedpost->parentid);
 163          if ($istimed && ($addtime > 0)) {
 164              $this->assertEquals($now + $addtime, $exportedpost->timecreated);
 165          } else {
 166              $this->assertEquals($now, $exportedpost->timecreated);
 167          }
 168          $this->assertEquals(null, $exportedpost->unread);
 169          $this->assertEquals(false, $exportedpost->isdeleted);
 170          $this->assertEquals($canview, $exportedpost->capabilities['view']);
 171          $this->assertEquals($canedit, $exportedpost->capabilities['edit']);
 172          $this->assertEquals($candelete, $exportedpost->capabilities['delete']);
 173          $this->assertEquals($cansplit, $exportedpost->capabilities['split']);
 174          $this->assertEquals($canreply, $exportedpost->capabilities['reply']);
 175          $this->assertEquals($canexport, $exportedpost->capabilities['export']);
 176          $this->assertEquals($canenrol, $exportedpost->capabilities['selfenrol']);
 177          $this->assertEquals($cancontrolreadstatus, $exportedpost->capabilities['controlreadstatus']);
 178          $this->assertNotEmpty($exportedpost->urls['view']);
 179          $this->assertNotEmpty($exportedpost->urls['viewisolated']);
 180          $this->assertNotEmpty($exportedpost->urls['edit']);
 181          $this->assertNotEmpty($exportedpost->urls['delete']);
 182          $this->assertNotEmpty($exportedpost->urls['split']);
 183          $this->assertNotEmpty($exportedpost->urls['reply']);
 184          $this->assertNotEmpty($exportedpost->urls['markasread']);
 185          $this->assertNotEmpty($exportedpost->urls['markasunread']);
 186          $this->assertCount(1, $exportedpost->attachments);
 187          $this->assertEquals('example1.jpg', $exportedpost->attachments[0]->filename);
 188          $this->assertCount(2, $exportedpost->tags);
 189          $this->assertEquals('foo', $exportedpost->tags[0]['displayname']);
 190          $this->assertEquals('bar', $exportedpost->tags[1]['displayname']);
 191          $this->assertEquals(null, $exportedpost->html['rating']);
 192          $this->assertNotEquals(null, $exportedpost->html['taglist']);
 193          $this->assertNotEmpty($exportedpost->html['authorsubheading']);
 194      }
 195  
 196      /**
 197       * Data provider for test_export_post().
 198       *
 199       * @return array
 200       */
 201      public function export_post_provider(): array {
 202          return [
 203              'Simple export' => [
 204              ],
 205              'Test timed post future' => [
 206                  true,
 207                  1000
 208              ],
 209              'Test timed post past' => [
 210                  true,
 211                  -1000
 212              ],
 213          ];
 214      }
 215  
 216      /**
 217       * Test exporting of a deleted post.
 218       */
 219      public function test_export_deleted_post() {
 220          global $CFG, $PAGE;
 221          $this->resetAfterTest();
 222  
 223          $CFG->enableportfolios = true;
 224          $filestorage = get_file_storage();
 225          $renderer = $PAGE->get_renderer('core');
 226          $datagenerator = $this->getDataGenerator();
 227          $forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
 228          $user = $datagenerator->create_user();
 229          $course = $datagenerator->create_course();
 230          $forum = $datagenerator->create_module('forum', ['course' => $course->id]);
 231          $coursemodule = get_coursemodule_from_instance('forum', $forum->id);
 232          $context = context_module::instance($coursemodule->id);
 233          $discussion = $forumgenerator->create_discussion((object) [
 234              'course' => $forum->course,
 235              'userid' => $user->id,
 236              'forum' => $forum->id
 237          ]);
 238          $now = time();
 239          $post = $forumgenerator->create_post((object) [
 240              'discussion' => $discussion->id,
 241              'parent' => 0,
 242              'userid' => $user->id,
 243              'created' => $now,
 244              'modified' => $now,
 245              'subject' => 'This is the subject',
 246              'message' => 'This is the message',
 247              'messagetrust' => 1,
 248              'attachment' => 0,
 249              'totalscore' => 0,
 250              'mailnow' => 1,
 251              'deleted' => 1
 252          ]);
 253  
 254          \core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id, $context, ['foo', 'bar']);
 255          $tags = \core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id);
 256          $attachment = $filestorage->create_file_from_string(
 257              [
 258                  'contextid' => $context->id,
 259                  'component' => 'mod_forum',
 260                  'filearea'  => 'attachment',
 261                  'itemid'    => $post->id,
 262                  'filepath'  => '/',
 263                  'filename'  => 'example1.jpg',
 264              ],
 265              'image contents'
 266          );
 267  
 268          $canview = true;
 269          $canedit = true;
 270          $candelete = true;
 271          $cansplit = true;
 272          $canreply = true;
 273          $canexport = true;
 274          $cancontrolreadstatus = true;
 275          $capabilitymanager = new test_capability_manager(
 276              $canview,
 277              $canedit,
 278              $candelete,
 279              $cansplit,
 280              $canreply,
 281              $canexport,
 282              $cancontrolreadstatus
 283          );
 284          $managerfactory = \mod_forum\local\container::get_manager_factory();
 285          $entityfactory = \mod_forum\local\container::get_entity_factory();
 286          $forum = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
 287          $discussion = $entityfactory->get_discussion_from_stdclass($discussion);
 288          $post = $entityfactory->get_post_from_stdclass($post);
 289          $author = $entityfactory->get_author_from_stdclass($user);
 290          $authorcontext = context_user::instance($author->get_id());
 291  
 292          $exporter = new post_exporter($post, [
 293              'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
 294              'capabilitymanager' => $capabilitymanager,
 295              'readreceiptcollection' => null,
 296              'urlfactory' => \mod_forum\local\container::get_url_factory(),
 297              'forum' => $forum,
 298              'discussion' => $discussion,
 299              'author' => $author,
 300              'authorcontextid' => $authorcontext->id,
 301              'user' => $user,
 302              'context' => $context,
 303              'authorgroups' => [],
 304              'attachments' => [$attachment],
 305              'tags' => $tags,
 306              'rating' => null,
 307              'includehtml' => true
 308          ]);
 309  
 310          $exportedpost = $exporter->export($renderer);
 311  
 312          $this->assertNotEquals('This is the subject', $exportedpost->subject);
 313          $this->assertNotEquals('This is the message', $exportedpost->message);
 314          $this->assertEquals(null, $exportedpost->timecreated);
 315          $this->assertEquals(null, $exportedpost->unread);
 316          $this->assertEquals(true, $exportedpost->isdeleted);
 317          $this->assertEquals([], $exportedpost->attachments);
 318          $this->assertEquals([], $exportedpost->tags);
 319          $this->assertEquals(null, $exportedpost->html['rating']);
 320          $this->assertEquals(null, $exportedpost->html['taglist']);
 321          $this->assertEquals(null, $exportedpost->html['authorsubheading']);
 322      }
 323  
 324      /**
 325       * Test exporting of a post the user can't view.
 326       */
 327      public function test_export_post_no_view_capability() {
 328          global $CFG, $PAGE;
 329          $this->resetAfterTest();
 330  
 331          $CFG->enableportfolios = true;
 332          $filestorage = get_file_storage();
 333          $renderer = $PAGE->get_renderer('core');
 334          $datagenerator = $this->getDataGenerator();
 335          $forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
 336          $user = $datagenerator->create_user();
 337          $course = $datagenerator->create_course();
 338          $forum = $datagenerator->create_module('forum', ['course' => $course->id]);
 339          $coursemodule = get_coursemodule_from_instance('forum', $forum->id);
 340          $context = context_module::instance($coursemodule->id);
 341          $discussion = $forumgenerator->create_discussion((object) [
 342              'course' => $forum->course,
 343              'userid' => $user->id,
 344              'forum' => $forum->id
 345          ]);
 346          $now = time();
 347          $post = $forumgenerator->create_post((object) [
 348              'discussion' => $discussion->id,
 349              'parent' => 0,
 350              'userid' => $user->id,
 351              'created' => $now,
 352              'modified' => $now,
 353              'subject' => 'This is the subject',
 354              'message' => 'This is the message',
 355              'messagetrust' => 1,
 356              'attachment' => 0,
 357              'totalscore' => 0,
 358              'mailnow' => 1,
 359              'deleted' => 0
 360          ]);
 361  
 362          \core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id, $context, ['foo', 'bar']);
 363          $tags = \core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id);
 364          $attachment = $filestorage->create_file_from_string(
 365              [
 366                  'contextid' => $context->id,
 367                  'component' => 'mod_forum',
 368                  'filearea'  => 'attachment',
 369                  'itemid'    => $post->id,
 370                  'filepath'  => '/',
 371                  'filename'  => 'example1.jpg',
 372              ],
 373              'image contents'
 374          );
 375  
 376          $canview = false;
 377          $canedit = true;
 378          $candelete = true;
 379          $cansplit = true;
 380          $canreply = true;
 381          $canexport = true;
 382          $cancontrolreadstatus = true;
 383          $capabilitymanager = new test_capability_manager(
 384              $canview,
 385              $canedit,
 386              $candelete,
 387              $cansplit,
 388              $canreply,
 389              $canexport,
 390              $cancontrolreadstatus
 391          );
 392          $managerfactory = \mod_forum\local\container::get_manager_factory();
 393          $entityfactory = \mod_forum\local\container::get_entity_factory();
 394          $forum = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
 395          $discussion = $entityfactory->get_discussion_from_stdclass($discussion);
 396          $post = $entityfactory->get_post_from_stdclass($post);
 397          $author = $entityfactory->get_author_from_stdclass($user);
 398          $authorcontext = context_user::instance($author->get_id());
 399  
 400          $exporter = new post_exporter($post, [
 401              'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
 402              'capabilitymanager' => $capabilitymanager,
 403              'readreceiptcollection' => null,
 404              'urlfactory' => \mod_forum\local\container::get_url_factory(),
 405              'forum' => $forum,
 406              'discussion' => $discussion,
 407              'author' => $author,
 408              'authorcontextid' => $authorcontext->id,
 409              'user' => $user,
 410              'context' => $context,
 411              'authorgroups' => [],
 412              'attachments' => [$attachment],
 413              'tags' => $tags,
 414              'rating' => null,
 415              'includehtml' => true
 416          ]);
 417  
 418          $exportedpost = $exporter->export($renderer);
 419  
 420          $this->assertNotEquals('This is the subject', $exportedpost->subject);
 421          $this->assertNotEquals('This is the message', $exportedpost->message);
 422          $this->assertEquals(null, $exportedpost->timecreated);
 423          $this->assertEquals(null, $exportedpost->unread);
 424          $this->assertEquals(false, $exportedpost->isdeleted);
 425          $this->assertEquals([], $exportedpost->attachments);
 426          $this->assertEquals([], $exportedpost->tags);
 427          $this->assertEquals(null, $exportedpost->html['rating']);
 428          $this->assertEquals(null, $exportedpost->html['taglist']);
 429          $this->assertEquals(null, $exportedpost->html['authorsubheading']);
 430      }
 431  }
 432  
 433  /**
 434   * Test implementation of the capability manager.
 435   *
 436   * @copyright  2019 Ryan Wyllie <ryan@moodle.com>
 437   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 438   */
 439  class test_capability_manager extends capability_manager {
 440      /** @var bool $view Value for can_view_post */
 441      private $view;
 442      /** @var bool $edit Value for can_edit_post */
 443      private $edit;
 444      /** @var bool $delete Value for can_delete_post */
 445      private $delete;
 446      /** @var bool $split Value for can_split_post */
 447      private $split;
 448      /** @var bool $reply Value for can_reply_to_post */
 449      private $reply;
 450      /** @var bool $export Value for can_export_post */
 451      private $export;
 452      /** @var bool $controlreadstatus Value for can_manually_control_post_read_status */
 453      private $controlreadstatus;
 454      /** @var bool $controlreadstatus Value for can_reply_privately_to_post */
 455      private $canreplyprivatelytopost;
 456      /** @var bool $canenrol Value for can_self_enrol */
 457      private $canenrol;
 458  
 459      /**
 460       * Constructor.
 461       *
 462       * @param bool $view Value for can_view_post
 463       * @param bool $edit Value for can_edit_post
 464       * @param bool $delete Value for can_delete_post
 465       * @param bool $split Value for can_split_post
 466       * @param bool $reply Value for can_reply_to_post
 467       * @param bool $export Value for can_export_post
 468       * @param bool $controlreadstatus Value for can_manually_control_post_read_status
 469       */
 470      public function __construct(
 471          bool $view = true,
 472          bool $edit = true,
 473          bool $delete = true,
 474          bool $split = true,
 475          bool $reply = true,
 476          bool $export = true,
 477          bool $controlreadstatus = true,
 478          bool $canreplyprivatelytopost = true,
 479          bool $canenrol = true
 480      ) {
 481          $this->view = $view;
 482          $this->edit = $edit;
 483          $this->delete = $delete;
 484          $this->split = $split;
 485          $this->reply = $reply;
 486          $this->export = $export;
 487          $this->controlreadstatus = $controlreadstatus;
 488          $this->canreplyprivatelytopost = $canreplyprivatelytopost;
 489          $this->canenrol = $canenrol;
 490      }
 491  
 492      /**
 493       * Override can_view_post
 494       *
 495       * @param stdClass $user The user
 496       * @param discussion_entity $discussion The discussion
 497       * @param post_entity $post The post
 498       * @return bool
 499       */
 500      public function can_view_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
 501          return $this->view;
 502      }
 503  
 504      /**
 505       * Override can_edit_post
 506       *
 507       * @param stdClass $user The user
 508       * @param discussion_entity $discussion The discussion
 509       * @param post_entity $post The post
 510       * @return bool
 511       */
 512      public function can_edit_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
 513          return $this->edit;
 514      }
 515  
 516      /**
 517       * Override can_delete_post
 518       *
 519       * @param stdClass $user The user
 520       * @param discussion_entity $discussion The discussion
 521       * @param post_entity $post The post
 522       * @param bool $hasreplies
 523       * @return bool
 524       */
 525      public function can_delete_post(stdClass $user, discussion_entity $discussion, post_entity $post,
 526                                      bool $hasreplies = false) : bool {
 527          return $this->delete;
 528      }
 529  
 530      /**
 531       * Override can_split_post
 532       *
 533       * @param stdClass $user The user
 534       * @param discussion_entity $discussion The discussion
 535       * @param post_entity $post The post
 536       * @return bool
 537       */
 538      public function can_split_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
 539          return $this->split;
 540      }
 541  
 542      /**
 543       * Override can_reply_to_post
 544       *
 545       * @param stdClass $user The user
 546       * @param discussion_entity $discussion The discussion
 547       * @param post_entity $post The post
 548       * @return bool
 549       */
 550      public function can_reply_to_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
 551          return $this->reply;
 552      }
 553  
 554      /**
 555       * Override can_export_post
 556       *
 557       * @param stdClass $user The user
 558       * @param post_entity $post The post
 559       * @return bool
 560       */
 561      public function can_export_post(stdClass $user, post_entity $post) : bool {
 562          return $this->export;
 563      }
 564  
 565      /**
 566       * Override can_manually_control_post_read_status
 567       *
 568       * @param stdClass $user The user
 569       * @return bool
 570       */
 571      public function can_manually_control_post_read_status(stdClass $user) : bool {
 572          return $this->controlreadstatus;
 573      }
 574  
 575      /**
 576       * Override can_reply_privately_to_post
 577       * @param stdClass $user
 578       * @param post_entity $post
 579       * @return bool
 580       */
 581      public function can_reply_privately_to_post(stdClass $user, post_entity $post) : bool {
 582          return $this->canreplyprivatelytopost;
 583      }
 584  
 585      /**
 586       * Override can_self_enrol
 587       * @param stdClass $user
 588       * @return bool
 589       */
 590      public function can_self_enrol(stdClass $user) : bool {
 591          return $this->canenrol;
 592      }
 593  }