Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

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