Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 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  namespace mod_forum;
  18  
  19  use mod_forum\local\container;
  20  use mod_forum\local\entities\forum;
  21  use mod_forum\local\managers\capability as capability_manager;
  22  use mod_forum_tests_generator_trait;
  23  
  24  defined('MOODLE_INTERNAL') || die();
  25  
  26  global $CFG;
  27  require_once (__DIR__ . '/generator_trait.php');
  28  
  29  /**
  30   * The capability manager tests.
  31   *
  32   * @package    mod_forum
  33   * @copyright  2019 Ryan Wyllie <ryan@moodle.com>
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   * @coversDefaultClass \mod_forum\local\managers\capability
  36   */
  37  class managers_capability_test extends \advanced_testcase {
  38      // Make use of the test generator trait.
  39      use mod_forum_tests_generator_trait;
  40  
  41      /** @var stdClass */
  42      private $user;
  43  
  44      /** @var \mod_forum\local\factories\entity */
  45      private $entityfactory;
  46  
  47      /** @var \mod_forum\local\factories\manager */
  48      private $managerfactory;
  49  
  50      /** @var stdClass */
  51      private $course;
  52  
  53      /** @var stdClass */
  54      private $forumrecord;
  55  
  56      /** @var stdClass */
  57      private $coursemodule;
  58  
  59      /** @var context */
  60      private $context;
  61  
  62      /** @var int */
  63      private $roleid;
  64  
  65      /** @var \mod_forum\local\entities\discussion */
  66      private $discussion;
  67  
  68      /** @var stdClass */
  69      private $discussionrecord;
  70  
  71      /** @var \mod_forum\local\entities\post */
  72      private $post;
  73  
  74      /** @var stdClass */
  75      private $postrecord;
  76  
  77      /**
  78       * Setup function before each test.
  79       */
  80      public function setUp(): void {
  81          global $DB;
  82  
  83          // We must clear the subscription caches. This has to be done both before each test, and after in case of other
  84          // tests using these functions.
  85          \mod_forum\subscriptions::reset_forum_cache();
  86  
  87          $datagenerator = $this->getDataGenerator();
  88          $this->user = $datagenerator->create_user();
  89          $this->managerfactory = container::get_manager_factory();
  90          $this->entityfactory = container::get_entity_factory();
  91          $this->course = $datagenerator->create_course();
  92          $this->forumrecord = $datagenerator->create_module('forum', ['course' => $this->course->id]);
  93          $this->coursemodule = get_coursemodule_from_instance('forum', $this->forumrecord->id);
  94          $this->context = \context_module::instance($this->coursemodule->id);
  95          $this->roleid = $DB->get_field('role', 'id', ['shortname' => 'teacher'], MUST_EXIST);
  96  
  97          $datagenerator->enrol_user($this->user->id, $this->course->id, 'teacher');
  98          [$discussion, $post] = $this->helper_post_to_forum($this->forumrecord, $this->user, ['timemodified' => time() - 100]);
  99          $this->discussion = $this->entityfactory->get_discussion_from_stdClass($discussion);
 100          $this->discussionrecord = $discussion;
 101          $this->post = $this->entityfactory->get_post_from_stdClass(
 102              (object) array_merge((array) $post, ['timecreated' => time() - 100])
 103          );
 104          $this->postrecord = $post;
 105  
 106          $this->setUser($this->user);
 107      }
 108  
 109      /**
 110       * Tear down function after each test.
 111       */
 112      public function tearDown(): void {
 113          // We must clear the subscription caches. This has to be done both before each test, and after in case of other
 114          // tests using these functions.
 115          \mod_forum\subscriptions::reset_forum_cache();
 116      }
 117  
 118      /**
 119       * Helper function to create a forum entity.
 120       *
 121       * @param array $forumproperties List of properties to override the prebuilt forum
 122       * @return forum
 123       */
 124      private function create_forum(array $forumproperties = []) {
 125          $forumrecord = (object) array_merge((array) $this->forumrecord, $forumproperties);
 126          return $this->entityfactory->get_forum_from_stdClass(
 127              $forumrecord,
 128              $this->context,
 129              $this->coursemodule,
 130              $this->course
 131          );
 132      }
 133  
 134      /**
 135       * Helper function to assign a capability to the prebuilt role (teacher).
 136       *
 137       * @param string $capability Name of the capability
 138       * @param context|null $context The context to assign the capability in
 139       */
 140      private function give_capability($capability, $context = null) {
 141          $context = $context ?? $this->context;
 142          assign_capability($capability, CAP_ALLOW, $this->roleid, $context->id, true);
 143      }
 144  
 145      /**
 146       * Helper function to prevent a capability to the prebuilt role (teacher).
 147       *
 148       * @param string $capability Name of the capability
 149       * @param context|null $context The context to assign the capability in
 150       */
 151      private function prevent_capability($capability, $context = null) {
 152          $context = $context ?? $this->context;
 153          assign_capability($capability, CAP_PREVENT, $this->roleid, $context->id, true);
 154      }
 155  
 156      /**
 157       * Test can_subscribe_to_forum.
 158       *
 159       * @covers ::can_subscribe_to_forum
 160       */
 161      public function test_can_subscribe_to_forum() {
 162          $this->resetAfterTest();
 163  
 164          $forum = $this->create_forum();
 165          $guestuser = $this->getDataGenerator()->create_user();
 166          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 167  
 168          $this->assertFalse($capabilitymanager->can_subscribe_to_forum($guestuser));
 169          $this->assertTrue($capabilitymanager->can_subscribe_to_forum($this->user));
 170      }
 171  
 172      /**
 173       * Test can_create_discussions.
 174       *
 175       * @covers ::can_create_discussions
 176       */
 177      public function test_can_create_discussions() {
 178          $this->resetAfterTest();
 179  
 180          $forum = $this->create_forum();
 181          $guestuser = $this->getDataGenerator()->create_user();
 182          $user = $this->user;
 183          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 184  
 185          $this->assertFalse($capabilitymanager->can_create_discussions($guestuser));
 186  
 187          $this->prevent_capability('mod/forum:startdiscussion');
 188          $this->assertFalse($capabilitymanager->can_create_discussions($user));
 189  
 190          $this->give_capability('mod/forum:startdiscussion');
 191          $this->assertTrue($capabilitymanager->can_create_discussions($user));
 192  
 193          $forum = $this->create_forum(['type' => 'news']);
 194          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 195  
 196          $this->prevent_capability('mod/forum:addnews');
 197          $this->assertFalse($capabilitymanager->can_create_discussions($user));
 198  
 199          $this->give_capability('mod/forum:addnews');
 200          $this->assertTrue($capabilitymanager->can_create_discussions($user));
 201  
 202          $forum = $this->create_forum(['type' => 'qanda']);
 203          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 204  
 205          $this->prevent_capability('mod/forum:addquestion');
 206          $this->assertFalse($capabilitymanager->can_create_discussions($user));
 207  
 208          $this->give_capability('mod/forum:addquestion');
 209          $this->assertTrue($capabilitymanager->can_create_discussions($user));
 210  
 211          // Test a forum in group mode.
 212          $forumrecord = $this->getDataGenerator()->create_module(
 213              'forum',
 214              ['course' => $this->course->id, 'groupmode' => SEPARATEGROUPS]
 215          );
 216          $coursemodule = get_coursemodule_from_instance('forum', $forumrecord->id);
 217          $context = \context_module::instance($coursemodule->id);
 218          $forum = $this->entityfactory->get_forum_from_stdClass(
 219              $forumrecord,
 220              $context,
 221              $coursemodule,
 222              $this->course
 223          );
 224  
 225          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 226  
 227          $this->assertFalse($capabilitymanager->can_create_discussions($user));
 228  
 229          $this->give_capability('moodle/site:accessallgroups', $context);
 230          $this->assertTrue($capabilitymanager->can_create_discussions($user));
 231  
 232          $this->prevent_capability('moodle/site:accessallgroups', $context);
 233          $this->assertFalse($capabilitymanager->can_create_discussions($user));
 234  
 235          $group = $this->getDataGenerator()->create_group(['courseid' => $this->course->id]);
 236          $this->getDataGenerator()->create_group_member(['userid' => $user->id, 'groupid' => $group->id]);
 237  
 238          $this->assertTrue($capabilitymanager->can_create_discussions($user, $group->id));
 239  
 240          // Test if cut off date is reached.
 241          $now = time();
 242          $forum = $this->create_forum(['cutoffdate' => $now + 86400 , 'blockafter' => 5, 'blockperiod' => 86400]);
 243          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 244          $this->prevent_capability('mod/forum:postwithoutthrottling');
 245          $this->assertTrue($capabilitymanager->can_create_discussions($user));
 246  
 247          $forum = $this->create_forum(['cutoffdate' => $now + 86400 , 'blockafter' => 1, 'blockperiod' => 86400]);
 248          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 249          $this->prevent_capability('mod/forum:postwithoutthrottling');
 250          $this->assertFalse($capabilitymanager->can_create_discussions($user));
 251      }
 252  
 253      /**
 254       * Test can_access_all_groups.
 255       *
 256       * @covers ::can_access_all_groups
 257       */
 258      public function test_can_access_all_groups() {
 259          $this->resetAfterTest();
 260  
 261          $forum = $this->create_forum();
 262          $user = $this->user;
 263          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 264  
 265          $this->prevent_capability('moodle/site:accessallgroups');
 266          $this->assertFalse($capabilitymanager->can_access_all_groups($user));
 267  
 268          $this->give_capability('moodle/site:accessallgroups');
 269          $this->assertTrue($capabilitymanager->can_access_all_groups($user));
 270      }
 271  
 272      /**
 273       * Test can_access_group.
 274       *
 275       * @covers ::can_access_group
 276       */
 277      public function test_can_access_group() {
 278          $this->resetAfterTest();
 279  
 280          $forum = $this->create_forum();
 281          $user = $this->user;
 282          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 283          $group = $this->getDataGenerator()->create_group(['courseid' => $this->course->id]);
 284  
 285          $this->prevent_capability('moodle/site:accessallgroups');
 286          $this->assertFalse($capabilitymanager->can_access_group($user, $group->id));
 287  
 288          $this->give_capability('moodle/site:accessallgroups');
 289          $this->assertTrue($capabilitymanager->can_access_group($user, $group->id));
 290  
 291          $this->prevent_capability('moodle/site:accessallgroups');
 292          $this->getDataGenerator()->create_group_member(['userid' => $user->id, 'groupid' => $group->id]);
 293          $this->assertTrue($capabilitymanager->can_access_group($user, $group->id));
 294      }
 295  
 296      /**
 297       * Test can_view_discussions.
 298       *
 299       * @covers ::can_view_discussions
 300       */
 301      public function test_can_view_discussions() {
 302          $this->resetAfterTest();
 303  
 304          $forum = $this->create_forum();
 305          $user = $this->user;
 306          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 307  
 308          $this->prevent_capability('mod/forum:viewdiscussion');
 309          $this->assertFalse($capabilitymanager->can_view_discussions($user));
 310  
 311          $this->give_capability('mod/forum:viewdiscussion');
 312          $this->assertTrue($capabilitymanager->can_view_discussions($user));
 313      }
 314  
 315      /**
 316       * Test can_move_discussions.
 317       *
 318       * @covers ::can_move_discussions
 319       */
 320      public function test_can_move_discussions() {
 321          $this->resetAfterTest();
 322  
 323          $forum = $this->create_forum();
 324          $user = $this->user;
 325          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 326  
 327          $this->prevent_capability('mod/forum:movediscussions');
 328          $this->assertFalse($capabilitymanager->can_move_discussions($user));
 329  
 330          $this->give_capability('mod/forum:movediscussions');
 331          $this->assertTrue($capabilitymanager->can_move_discussions($user));
 332  
 333          $forum = $this->create_forum(['type' => 'single']);
 334          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 335  
 336          $this->assertFalse($capabilitymanager->can_move_discussions($user));
 337      }
 338  
 339      /**
 340       * Test can_pin_discussions.
 341       *
 342       * @covers ::can_pin_discussions
 343       */
 344      public function test_can_pin_discussions() {
 345          $this->resetAfterTest();
 346  
 347          $forum = $this->create_forum();
 348          $user = $this->user;
 349          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 350  
 351          $this->prevent_capability('mod/forum:pindiscussions');
 352          $this->assertFalse($capabilitymanager->can_pin_discussions($user));
 353  
 354          $this->give_capability('mod/forum:pindiscussions');
 355          $this->assertTrue($capabilitymanager->can_pin_discussions($user));
 356      }
 357  
 358      /**
 359       * Test can_split_discussions.
 360       *
 361       * @covers ::can_split_discussions
 362       */
 363      public function test_can_split_discussions() {
 364          $this->resetAfterTest();
 365  
 366          $forum = $this->create_forum();
 367          $user = $this->user;
 368          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 369  
 370          $this->prevent_capability('mod/forum:splitdiscussions');
 371          $this->assertFalse($capabilitymanager->can_split_discussions($user));
 372  
 373          $this->give_capability('mod/forum:splitdiscussions');
 374          $this->assertTrue($capabilitymanager->can_split_discussions($user));
 375  
 376          $forum = $this->create_forum(['type' => 'single']);
 377          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 378  
 379          $this->assertFalse($capabilitymanager->can_split_discussions($user));
 380      }
 381  
 382      /**
 383       * Test can_export_discussions.
 384       *
 385       * @covers ::can_export_discussions
 386       */
 387      public function test_can_export_discussions() {
 388          global $CFG;
 389          $this->resetAfterTest();
 390  
 391          $CFG->enableportfolios = true;
 392          $forum = $this->create_forum();
 393          $user = $this->user;
 394          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 395  
 396          $this->prevent_capability('mod/forum:exportdiscussion');
 397          $this->assertFalse($capabilitymanager->can_export_discussions($user));
 398  
 399          $this->give_capability('mod/forum:exportdiscussion');
 400          $this->assertTrue($capabilitymanager->can_export_discussions($user));
 401  
 402          $CFG->enableportfolios = false;
 403  
 404          $this->assertFalse($capabilitymanager->can_export_discussions($user));
 405      }
 406  
 407      /**
 408       * Test can_manually_control_post_read_status.
 409       *
 410       * @covers ::can_manually_control_post_read_status
 411       */
 412      public function test_can_manually_control_post_read_status() {
 413          global $CFG, $DB;
 414          $this->resetAfterTest();
 415  
 416          $CFG->forum_usermarksread = true;
 417          $forum = $this->create_forum();
 418          $user = $this->user;
 419          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 420          $cache = \cache::make('mod_forum', 'forum_is_tracked');
 421  
 422          $user->trackforums = true;
 423          $prefid = $DB->insert_record('forum_track_prefs', ['userid' => $user->id, 'forumid' => $forum->get_id()]);
 424          $this->assertFalse($capabilitymanager->can_manually_control_post_read_status($user));
 425          $cache->purge();
 426  
 427          $DB->delete_records('forum_track_prefs', ['id' => $prefid]);
 428          $this->assertTrue($capabilitymanager->can_manually_control_post_read_status($user));
 429          $cache->purge();
 430  
 431          $CFG->forum_usermarksread = false;
 432  
 433          $this->assertFalse($capabilitymanager->can_manually_control_post_read_status($user));
 434      }
 435  
 436      /**
 437       * Test must_post_before_viewing_discussion.
 438       *
 439       * @covers ::must_post_before_viewing_discussion
 440       */
 441      public function test_must_post_before_viewing_discussion() {
 442          $this->resetAfterTest();
 443  
 444          $forum = $this->create_forum();
 445          $user = $this->user;
 446          $discussion = $this->discussion;
 447          $newuser = $this->getDataGenerator()->create_user();
 448          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 449          $this->getDataGenerator()->enrol_user($newuser->id, $this->course->id, 'teacher');
 450  
 451          $this->assertFalse($capabilitymanager->must_post_before_viewing_discussion($newuser, $discussion));
 452  
 453          $forum = $this->create_forum(['type' => 'qanda']);
 454          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 455  
 456          $this->prevent_capability('mod/forum:viewqandawithoutposting');
 457          $this->assertTrue($capabilitymanager->must_post_before_viewing_discussion($newuser, $discussion));
 458  
 459          $this->give_capability('mod/forum:viewqandawithoutposting');
 460          $this->assertFalse($capabilitymanager->must_post_before_viewing_discussion($newuser, $discussion));
 461  
 462          $this->prevent_capability('mod/forum:viewqandawithoutposting');
 463          // The pre-generated user has a pre-generated post in the disussion already.
 464          $this->assertFalse($capabilitymanager->must_post_before_viewing_discussion($user, $discussion));
 465      }
 466  
 467      /**
 468       * Test can_subscribe_to_discussion.
 469       *
 470       * @covers ::can_subscribe_to_discussion
 471       */
 472      public function test_can_subscribe_to_discussion() {
 473          $this->resetAfterTest();
 474  
 475          $forum = $this->create_forum();
 476          $discussion = $this->discussion;
 477          $guestuser = $this->getDataGenerator()->create_user();
 478          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 479  
 480          $this->assertFalse($capabilitymanager->can_subscribe_to_discussion($guestuser, $discussion));
 481          $this->assertTrue($capabilitymanager->can_subscribe_to_discussion($this->user, $discussion));
 482      }
 483  
 484      /**
 485       * Test can_move_discussion.
 486       *
 487       * @covers ::can_move_discussion
 488       */
 489      public function test_can_move_discussion() {
 490          $this->resetAfterTest();
 491  
 492          $forum = $this->create_forum();
 493          $discussion = $this->discussion;
 494          $user = $this->user;
 495          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 496  
 497          $this->prevent_capability('mod/forum:movediscussions');
 498          $this->assertFalse($capabilitymanager->can_move_discussion($user, $discussion));
 499  
 500          $this->give_capability('mod/forum:movediscussions');
 501          $this->assertTrue($capabilitymanager->can_move_discussion($user, $discussion));
 502  
 503          $forum = $this->create_forum(['type' => 'single']);
 504          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 505  
 506          $this->assertFalse($capabilitymanager->can_move_discussion($user, $discussion));
 507      }
 508  
 509      /**
 510       * Test can_pin_discussion.
 511       *
 512       * @covers ::can_pin_discussion
 513       */
 514      public function test_can_pin_discussion() {
 515          $this->resetAfterTest();
 516  
 517          $forum = $this->create_forum();
 518          $discussion = $this->discussion;
 519          $user = $this->user;
 520          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 521  
 522          $this->prevent_capability('mod/forum:pindiscussions');
 523          $this->assertFalse($capabilitymanager->can_pin_discussion($user, $discussion));
 524  
 525          $this->give_capability('mod/forum:pindiscussions');
 526          $this->assertTrue($capabilitymanager->can_pin_discussion($user, $discussion));
 527      }
 528  
 529      /**
 530       * Test can_post_in_discussion.
 531       *
 532       * @covers ::can_post_in_discussion
 533       */
 534      public function test_can_post_in_discussion() {
 535          $this->resetAfterTest();
 536  
 537          $discussion = $this->discussion;
 538          $user = $this->user;
 539  
 540          // Locked discussions.
 541          $lockedforum = $this->create_forum(['lockdiscussionafter' => 1]);
 542          $capabilitymanager = $this->managerfactory->get_capability_manager($lockedforum);
 543  
 544          $this->give_capability('mod/forum:canoverridediscussionlock');
 545          $this->assertTrue($capabilitymanager->can_post_in_discussion($user, $discussion));
 546  
 547          $this->prevent_capability('mod/forum:canoverridediscussionlock');
 548          $this->assertFalse($capabilitymanager->can_post_in_discussion($user, $discussion));
 549  
 550          // News forum.
 551          $newsforum = $this->create_forum(['type' => 'news']);
 552          $capabilitymanager = $this->managerfactory->get_capability_manager($newsforum);
 553  
 554          $this->give_capability('mod/forum:replynews');
 555          $this->assertTrue($capabilitymanager->can_post_in_discussion($user, $discussion));
 556  
 557          $this->prevent_capability('mod/forum:replynews');
 558          $this->assertFalse($capabilitymanager->can_post_in_discussion($user, $discussion));
 559  
 560          // General forum.
 561          $forum = $this->create_forum();
 562          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 563  
 564          $this->give_capability('mod/forum:replypost');
 565          $this->assertTrue($capabilitymanager->can_post_in_discussion($user, $discussion));
 566  
 567          $this->prevent_capability('mod/forum:replypost');
 568          $this->assertFalse($capabilitymanager->can_post_in_discussion($user, $discussion));
 569  
 570          // Forum in separate group mode.
 571          $forumrecord = $this->getDataGenerator()->create_module(
 572              'forum',
 573              ['course' => $this->course->id, 'groupmode' => SEPARATEGROUPS]
 574          );
 575          $coursemodule = get_coursemodule_from_instance('forum', $forumrecord->id);
 576          $context = \context_module::instance($coursemodule->id);
 577          $forum = $this->entityfactory->get_forum_from_stdClass(
 578              $forumrecord,
 579              $context,
 580              $coursemodule,
 581              $this->course
 582          );
 583          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 584  
 585          $this->give_capability('moodle/site:accessallgroups', $context);
 586          $this->assertTrue($capabilitymanager->can_post_in_discussion($user, $discussion));
 587  
 588          $this->prevent_capability('moodle/site:accessallgroups', $context);
 589          $this->assertFalse($capabilitymanager->can_post_in_discussion($user, $discussion));
 590  
 591          $group = $this->getDataGenerator()->create_group(['courseid' => $this->course->id]);
 592          $discussion = $this->entityfactory->get_discussion_from_stdClass(
 593              (object) array_merge((array) $this->discussionrecord, ['groupid' => $group->id])
 594          );
 595  
 596          $this->assertFalse($capabilitymanager->can_post_in_discussion($user, $discussion));
 597  
 598          $this->getDataGenerator()->create_group_member(['userid' => $user->id, 'groupid' => $group->id]);
 599  
 600          $this->assertTrue($capabilitymanager->can_post_in_discussion($user, $discussion));
 601  
 602          // Forum in visible group mode.
 603          $forumrecord = $this->getDataGenerator()->create_module(
 604              'forum',
 605              ['course' => $this->course->id, 'groupmode' => VISIBLEGROUPS]
 606          );
 607          $coursemodule = get_coursemodule_from_instance('forum', $forumrecord->id);
 608          $context = \context_module::instance($coursemodule->id);
 609          $forum = $this->entityfactory->get_forum_from_stdClass(
 610              $forumrecord,
 611              $context,
 612              $coursemodule,
 613              $this->course
 614          );
 615          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 616  
 617          $this->give_capability('moodle/site:accessallgroups', $context);
 618          $this->assertTrue($capabilitymanager->can_post_in_discussion($user, $discussion));
 619  
 620          $this->prevent_capability('moodle/site:accessallgroups', $context);
 621          $this->assertTrue($capabilitymanager->can_post_in_discussion($user, $discussion));
 622  
 623          $group = $this->getDataGenerator()->create_group(['courseid' => $this->course->id]);
 624          $discussion = $this->entityfactory->get_discussion_from_stdClass(
 625              (object) array_merge((array) $this->discussionrecord, ['groupid' => $group->id])
 626          );
 627  
 628          $this->assertFalse($capabilitymanager->can_post_in_discussion($user, $discussion));
 629  
 630          $this->getDataGenerator()->create_group_member(['userid' => $user->id, 'groupid' => $group->id]);
 631  
 632          $this->assertTrue($capabilitymanager->can_post_in_discussion($user, $discussion));
 633  
 634          $now = time();
 635          $forum = $this->create_forum(['cutoffdate' => $now + 86400 , 'blockafter' => 5, 'blockperiod' => 86400]);
 636          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 637          $this->prevent_capability('mod/forum:postwithoutthrottling');
 638          $this->give_capability('mod/forum:replypost');
 639          $this->assertTrue($capabilitymanager->can_post_in_discussion($user, $discussion));
 640  
 641          $forum = $this->create_forum(['cutoffdate' => $now + 86400 , 'blockafter' => 1, 'blockperiod' => 86400]);
 642          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 643          $this->prevent_capability('mod/forum:postwithoutthrottling');
 644          $this->assertFalse($capabilitymanager->can_post_in_discussion($user, $discussion));
 645      }
 646  
 647      /**
 648       * Test can_edit_post.
 649       *
 650       * @covers ::can_edit_post
 651       */
 652      public function test_can_edit_post() {
 653          global $CFG;
 654  
 655          $this->resetAfterTest();
 656  
 657          $forum = $this->create_forum();
 658          $discussion = $this->discussion;
 659          // The generated post is created 100 seconds in the past.
 660          $post = $this->post;
 661          $user = $this->user;
 662          $otheruser = $this->getDataGenerator()->create_user();
 663          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 664  
 665          $this->prevent_capability('mod/forum:editanypost');
 666  
 667          // 200 seconds to edit.
 668          $CFG->maxeditingtime = 200;
 669          $this->assertTrue($capabilitymanager->can_edit_post($user, $discussion, $post));
 670  
 671          // Can not edit within editing time if $post->mailnow > 0 (selected).
 672          $CFG->maxeditingtime = 200;
 673          $post = $this->entityfactory->get_post_from_stdClass(
 674              (object) array_merge((array) $this->postrecord, ['mailnow' => 1])
 675          );
 676          $this->assertFalse($capabilitymanager->can_edit_post($user, $discussion, $post));
 677  
 678          // Back to normal - mailnow not selected.
 679          $post = $this->entityfactory->get_post_from_stdClass(
 680              (object) array_merge((array) $this->postrecord, ['mailnow' => 0])
 681          );
 682  
 683          // 10 seconds to edit. No longer in editing time.
 684          $CFG->maxeditingtime = 10;
 685          $this->assertFalse($capabilitymanager->can_edit_post($user, $discussion, $post));
 686  
 687          // Can edit outside of editing time with this capability.
 688          $this->give_capability('mod/forum:editanypost');
 689          $this->assertTrue($capabilitymanager->can_edit_post($user, $discussion, $post));
 690  
 691          $CFG->maxeditingtime = 200;
 692          $this->assertFalse($capabilitymanager->can_edit_post($otheruser, $discussion, $post));
 693  
 694          $this->prevent_capability('mod/forum:editanypost');
 695  
 696          // News forum.
 697          $forum = $this->create_forum(['type' => 'news']);
 698          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 699          // Discussion hasn't started yet.
 700          $discussion = $this->entityfactory->get_discussion_from_stdClass(
 701              (object) array_merge((array) $this->discussionrecord, ['timestart' => time() + 100])
 702          );
 703  
 704          $this->assertFalse($capabilitymanager->can_edit_post($user, $discussion, $post));
 705  
 706          // Back to a discussion that has started.
 707          $discussion = $this->discussion;
 708          // Post is a reply.
 709          $post = $this->entityfactory->get_post_from_stdClass(
 710              (object) array_merge((array) $this->postrecord, ['parent' => 5])
 711          );
 712  
 713          $this->assertFalse($capabilitymanager->can_edit_post($user, $discussion, $post));
 714  
 715          $post = $this->post;
 716          // Discussion has started and post isn't a reply so we can edit it.
 717          $this->assertTrue($capabilitymanager->can_edit_post($user, $discussion, $post));
 718  
 719          // Single forum.
 720          $forum = $this->create_forum(['type' => 'single']);
 721          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 722  
 723          // Create a new post that definitely isn't the first post of the discussion.
 724          // Only the author, and a user with editanypost can edit it.
 725          $post = $this->entityfactory->get_post_from_stdClass(
 726              (object) array_merge((array) $this->postrecord, ['id' => $post->get_id() + 100])
 727          );
 728          $this->give_capability('mod/forum:editanypost');
 729          $this->assertTrue($capabilitymanager->can_edit_post($user, $discussion, $post));
 730          $this->assertFalse($capabilitymanager->can_edit_post($otheruser, $discussion, $post));
 731  
 732          $post = $this->post;
 733          // Set the first post of the discussion to our post.
 734          $discussion = $this->entityfactory->get_discussion_from_stdClass(
 735              (object) array_merge((array) $this->discussionrecord, ['firstpost' => $post->get_id()])
 736          );
 737  
 738          $this->prevent_capability('moodle/course:manageactivities');
 739          $this->assertFalse($capabilitymanager->can_edit_post($user, $discussion, $post));
 740  
 741          $this->give_capability('moodle/course:manageactivities');
 742          $this->assertTrue($capabilitymanager->can_edit_post($user, $discussion, $post));
 743      }
 744  
 745      /**
 746       * Test can_delete_post.
 747       *
 748       * @covers ::can_delete_post
 749       */
 750      public function test_can_delete_post() {
 751          global $CFG;
 752  
 753          $this->resetAfterTest();
 754  
 755          // Single forum.
 756          $forum = $this->create_forum(['type' => 'single']);
 757          // The generated post is created 100 seconds in the past.
 758          $post = $this->post;
 759          $user = $this->user;
 760          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 761  
 762          // Set the first post of the discussion to our post.
 763          $discussion = $this->entityfactory->get_discussion_from_stdClass(
 764              (object) array_merge((array) $this->discussionrecord, ['firstpost' => $post->get_id()])
 765          );
 766  
 767          // Can't delete the first post of a single discussion forum.
 768          $this->assertFalse($capabilitymanager->can_delete_post($user, $discussion, $post));
 769  
 770          // Set the first post of the discussion to something else.
 771          $discussion = $this->entityfactory->get_discussion_from_stdClass(
 772              (object) array_merge((array) $this->discussionrecord, ['firstpost' => $post->get_id() - 1])
 773          );
 774  
 775          $this->assertTrue($capabilitymanager->can_delete_post($user, $discussion, $post));
 776  
 777          // Back to a general forum.
 778          $forum = $this->create_forum();
 779          $this->prevent_capability('mod/forum:deleteanypost');
 780          $this->give_capability('mod/forum:deleteownpost');
 781          // 200 second editing time to make sure our post is still within it.
 782          $CFG->maxeditingtime = 200;
 783  
 784          // Can not delete within editing time if $post->mailnow > 0 (selected).
 785          $post = $this->entityfactory->get_post_from_stdClass(
 786              (object) array_merge((array) $this->postrecord, ['mailnow' => 1])
 787          );
 788          $this->assertFalse($capabilitymanager->can_delete_post($user, $discussion, $post));
 789  
 790          // Back to normal - mailnow not selected.
 791          $post = $this->entityfactory->get_post_from_stdClass(
 792              (object) array_merge((array) $this->postrecord, ['mailnow' => 0])
 793          );
 794  
 795          // Make the post owned by someone else.
 796          $post = $this->entityfactory->get_post_from_stdClass(
 797              (object) array_merge((array) $this->postrecord, ['userid' => $user->id - 1])
 798          );
 799  
 800          // Can't delete someone else's post.
 801          $this->assertFalse($capabilitymanager->can_delete_post($user, $discussion, $post));
 802          // Back to our post.
 803          $post = $this->post;
 804  
 805          // Not in editing time.
 806          $CFG->maxeditingtime = 10;
 807          $this->assertFalse($capabilitymanager->can_delete_post($user, $discussion, $post));
 808  
 809          $CFG->maxeditingtime = 200;
 810          // Remove the capability to delete own post.
 811          $this->prevent_capability('mod/forum:deleteownpost');
 812          $this->assertFalse($capabilitymanager->can_delete_post($user, $discussion, $post));
 813  
 814          $this->give_capability('mod/forum:deleteownpost');
 815          $this->assertTrue($capabilitymanager->can_delete_post($user, $discussion, $post));
 816  
 817          $this->give_capability('mod/forum:deleteanypost');
 818          $CFG->maxeditingtime = 10;
 819          $this->assertTrue($capabilitymanager->can_delete_post($user, $discussion, $post));
 820      }
 821  
 822      /**
 823       * Test can_split_post.
 824       *
 825       * @covers ::can_split_post
 826       */
 827      public function test_can_split_post() {
 828          $this->resetAfterTest();
 829  
 830          $forum = $this->create_forum();
 831          $user = $this->user;
 832          $discussion = $this->discussion;
 833          $post = $this->post;
 834          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 835  
 836          // Make the post a reply.
 837          $post = $this->entityfactory->get_post_from_stdClass(
 838              (object) array_merge((array) $this->postrecord, ['parent' => 5])
 839          );
 840  
 841          $this->prevent_capability('mod/forum:splitdiscussions');
 842          $this->assertFalse($capabilitymanager->can_split_post($user, $discussion, $post));
 843  
 844          $this->give_capability('mod/forum:splitdiscussions');
 845          $this->assertTrue($capabilitymanager->can_split_post($user, $discussion, $post));
 846  
 847          // Make the post have no parent.
 848          $post = $this->entityfactory->get_post_from_stdClass(
 849              (object) array_merge((array) $this->postrecord, ['parent' => 0])
 850          );
 851  
 852          $this->assertFalse($capabilitymanager->can_split_post($user, $discussion, $post));
 853  
 854          $forum = $this->create_forum(['type' => 'single']);
 855          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 856          // Make the post a reply.
 857          $post = $this->entityfactory->get_post_from_stdClass(
 858              (object) array_merge((array) $this->postrecord, ['parent' => 5])
 859          );
 860  
 861          // Can't split a single discussion forum.
 862          $this->assertFalse($capabilitymanager->can_split_post($user, $discussion, $post));
 863  
 864          // Make the post a private reply.
 865          $post = $this->entityfactory->get_post_from_stdClass(
 866              (object) array_merge((array) $this->postrecord, ['parent' => 5, 'privatereplyto' => $user->id])
 867          );
 868  
 869          // Can't split at a private reply.
 870          $this->assertFalse($capabilitymanager->can_split_post($user, $discussion, $post));
 871      }
 872  
 873      /**
 874       * Test can_reply_to_post.
 875       *
 876       * @covers ::can_reply_to_post
 877       */
 878      public function test_can_reply_to_post() {
 879          $this->resetAfterTest();
 880  
 881          $discussion = $this->discussion;
 882          $user = $this->user;
 883          $post = $this->post;
 884  
 885          // Locked discussions.
 886          $lockedforum = $this->create_forum(['lockdiscussionafter' => 1]);
 887          $capabilitymanager = $this->managerfactory->get_capability_manager($lockedforum);
 888  
 889          $this->give_capability('mod/forum:canoverridediscussionlock');
 890          $this->assertTrue($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 891  
 892          $this->prevent_capability('mod/forum:canoverridediscussionlock');
 893          $this->assertFalse($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 894  
 895          // News forum.
 896          $newsforum = $this->create_forum(['type' => 'news']);
 897          $capabilitymanager = $this->managerfactory->get_capability_manager($newsforum);
 898  
 899          $this->give_capability('mod/forum:replynews');
 900          $this->assertTrue($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 901  
 902          $this->prevent_capability('mod/forum:replynews');
 903          $this->assertFalse($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 904  
 905          // General forum.
 906          $forum = $this->create_forum();
 907          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 908  
 909          $this->give_capability('mod/forum:replypost');
 910          $this->assertTrue($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 911  
 912          $this->prevent_capability('mod/forum:replypost');
 913          $this->assertFalse($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 914  
 915          // Forum in separate group mode.
 916          $forumrecord = $this->getDataGenerator()->create_module(
 917              'forum',
 918              ['course' => $this->course->id, 'groupmode' => SEPARATEGROUPS]
 919          );
 920          $coursemodule = get_coursemodule_from_instance('forum', $forumrecord->id);
 921          $context = \context_module::instance($coursemodule->id);
 922          $forum = $this->entityfactory->get_forum_from_stdClass(
 923              $forumrecord,
 924              $context,
 925              $coursemodule,
 926              $this->course
 927          );
 928          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 929  
 930          $this->give_capability('moodle/site:accessallgroups', $context);
 931          $this->assertTrue($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 932  
 933          $this->prevent_capability('moodle/site:accessallgroups', $context);
 934          $this->assertFalse($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 935  
 936          $group = $this->getDataGenerator()->create_group(['courseid' => $this->course->id]);
 937          $discussion = $this->entityfactory->get_discussion_from_stdClass(
 938              (object) array_merge((array) $this->discussionrecord, ['groupid' => $group->id])
 939          );
 940  
 941          $this->assertFalse($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 942  
 943          $this->getDataGenerator()->create_group_member(['userid' => $user->id, 'groupid' => $group->id]);
 944  
 945          $this->assertTrue($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 946  
 947          // Forum in visible group mode.
 948          $forumrecord = $this->getDataGenerator()->create_module(
 949              'forum',
 950              ['course' => $this->course->id, 'groupmode' => VISIBLEGROUPS]
 951          );
 952          $coursemodule = get_coursemodule_from_instance('forum', $forumrecord->id);
 953          $context = \context_module::instance($coursemodule->id);
 954          $forum = $this->entityfactory->get_forum_from_stdClass(
 955              $forumrecord,
 956              $context,
 957              $coursemodule,
 958              $this->course
 959          );
 960          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
 961  
 962          $this->give_capability('moodle/site:accessallgroups', $context);
 963          $this->assertTrue($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 964  
 965          $this->prevent_capability('moodle/site:accessallgroups', $context);
 966          $this->assertTrue($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 967  
 968          $group = $this->getDataGenerator()->create_group(['courseid' => $this->course->id]);
 969          $discussion = $this->entityfactory->get_discussion_from_stdClass(
 970              (object) array_merge((array) $this->discussionrecord, ['groupid' => $group->id])
 971          );
 972  
 973          $this->assertFalse($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 974  
 975          $this->getDataGenerator()->create_group_member(['userid' => $user->id, 'groupid' => $group->id]);
 976  
 977          $this->assertTrue($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 978  
 979          // Make the post a private reply.
 980          $post = $this->entityfactory->get_post_from_stdClass(
 981              (object) array_merge((array) $this->postrecord, ['parent' => 5, 'privatereplyto' => $user->id])
 982          );
 983  
 984          // Can't reply to a a private reply.
 985          $this->assertFalse($capabilitymanager->can_reply_to_post($user, $discussion, $post));
 986      }
 987  
 988      /**
 989       * Test for \mod_forum\local\managers\capability::can_reply_to_post() involving Q & A forums.
 990       */
 991      public function test_can_reply_to_post_in_qanda_forum() {
 992          global $CFG;
 993  
 994          $this->resetAfterTest();
 995  
 996          // Set max editing time to 10 seconds.
 997          $CFG->maxeditingtime = 10;
 998  
 999          $qandaforum = $this->create_forum(['type' => 'qanda']);
1000          $datagenerator = $this->getDataGenerator();
1001          $capabilitymanager = $this->managerfactory->get_capability_manager($qandaforum);
1002  
1003          // Student 1.
1004          $student1 = $datagenerator->create_user(['firstname' => 'S1']);
1005          $datagenerator->enrol_user($student1->id, $this->course->id, 'student');
1006          // Confirm Student 1 can reply to the question.
1007          $this->assertTrue($capabilitymanager->can_reply_to_post($student1, $this->discussion, $this->post));
1008  
1009          // Student 2.
1010          $student2 = $datagenerator->create_user(['firstname' => 'S2']);
1011          $datagenerator->enrol_user($student2->id, $this->course->id, 'student');
1012          // Confirm Student 2 can reply to the question.
1013          $this->assertTrue($capabilitymanager->can_reply_to_post($student2, $this->discussion, $this->post));
1014  
1015          // Reply to the question as student 1.
1016          $now = time();
1017          $options = ['parent' => $this->post->get_id(), 'created' => $now - 100];
1018          $student1post = $this->helper_post_to_discussion($this->forumrecord, $this->discussionrecord, $student1, $options);
1019          $student1postentity = $this->entityfactory->get_post_from_stdClass($student1post);
1020  
1021          // Confirm Student 2 cannot reply student 1's answer yet.
1022          $this->assertFalse($capabilitymanager->can_reply_to_post($student2, $this->discussion, $student1postentity));
1023  
1024          // Reply to the question as student 2.
1025          $this->helper_post_to_discussion($this->forumrecord, $this->discussionrecord, $student2, $options);
1026  
1027          // Reinitialise capability manager first to ensure we don't return cached values.
1028          $capabilitymanager = $this->managerfactory->get_capability_manager($qandaforum);
1029  
1030          // Confirm Student 2 can now reply to student 1's answer.
1031          $this->assertTrue($capabilitymanager->can_reply_to_post($student2, $this->discussion, $student1postentity));
1032      }
1033  
1034      /**
1035       * Ensure that can_reply_privately_to_post works as expected.
1036       *
1037       * @covers ::can_reply_privately_to_post
1038       */
1039      public function test_can_reply_privately_to_post() {
1040          $this->resetAfterTest();
1041  
1042          $forum = $this->create_forum();
1043          $discussion = $this->discussion;
1044          $user = $this->user;
1045          $post = $this->post;
1046          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
1047  
1048          // Without the capability, and with a standard post, it is not possible to reply privately.
1049          $this->prevent_capability('mod/forum:postprivatereply');
1050          $this->assertFalse($capabilitymanager->can_reply_privately_to_post($this->user, $post));
1051  
1052          // With the capability, and a standard post, it is possible to reply privately.
1053          $this->give_capability('mod/forum:postprivatereply');
1054          $this->assertTrue($capabilitymanager->can_reply_privately_to_post($this->user, $post));
1055  
1056          // Make the post a private reply.
1057          $post = $this->entityfactory->get_post_from_stdClass(
1058              (object) array_merge((array) $this->postrecord, ['parent' => 5, 'privatereplyto' => $user->id])
1059          );
1060  
1061          // Can't ever reply to a a private reply.
1062          $this->assertFalse($capabilitymanager->can_reply_privately_to_post($user, $post));
1063      }
1064  
1065      /**
1066       * Ensure that can_view_post works as expected.
1067       *
1068       * @covers ::can_view_post
1069       */
1070      public function test_can_view_post() {
1071          $this->resetAfterTest();
1072  
1073          $forum = $this->create_forum();
1074          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
1075  
1076          $user = $this->user;
1077          $otheruser = $this->getDataGenerator()->create_user();
1078  
1079          $discussion = $this->discussion;
1080          $post = $this->post;
1081  
1082          $postproperties = ['parent' => $post->get_id(), 'userid' => $otheruser->id, 'privatereplyto' => $otheruser->id];
1083          $privatepost = $this->entityfactory->get_post_from_stdClass(
1084              (object) array_merge((array) $this->postrecord, $postproperties)
1085          );
1086  
1087          $this->prevent_capability('mod/forum:readprivatereplies');
1088          $this->assertFalse($capabilitymanager->can_view_post($user, $discussion, $privatepost));
1089      }
1090  
1091      /**
1092       * Ensure that can_view_post_shell considers private replies correctly.
1093       *
1094       * @covers ::can_view_post_shell
1095       */
1096      public function test_can_view_post_shell() {
1097          $this->resetAfterTest();
1098  
1099          $forum = $this->create_forum();
1100          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
1101  
1102          $user = $this->user;
1103          $otheruser = $this->getDataGenerator()->create_user();
1104  
1105          $discussion = $this->discussion;
1106          $post = $this->post;
1107  
1108          $postproperties = ['parent' => $post->get_id(), 'userid' => $user->id, 'privatereplyto' => $user->id];
1109          $privatepostfrommetome = $this->entityfactory->get_post_from_stdClass(
1110              (object) array_merge((array) $this->postrecord, $postproperties)
1111          );
1112  
1113          $postproperties = ['parent' => $post->get_id(), 'userid' => $user->id, 'privatereplyto' => $otheruser->id];
1114          $privatepostfrommetoother = $this->entityfactory->get_post_from_stdClass(
1115              (object) array_merge((array) $this->postrecord, $postproperties)
1116          );
1117  
1118          $postproperties = ['parent' => $post->get_id(), 'userid' => $otheruser->id, 'privatereplyto' => $user->id];
1119          $privatepostfromothertome = $this->entityfactory->get_post_from_stdClass(
1120              (object) array_merge((array) $this->postrecord, $postproperties)
1121          );
1122  
1123          $postproperties = ['parent' => $post->get_id(), 'userid' => $otheruser->id, 'privatereplyto' => $otheruser->id];
1124          $privatepostfromothertoother = $this->entityfactory->get_post_from_stdClass(
1125              (object) array_merge((array) $this->postrecord, $postproperties)
1126          );
1127  
1128          // Can always view public replies, and private replies by me or to me.
1129          $this->prevent_capability('mod/forum:readprivatereplies');
1130          $this->assertTrue($capabilitymanager->can_view_post_shell($this->user, $post));
1131          $this->assertTrue($capabilitymanager->can_view_post_shell($this->user, $privatepostfrommetome));
1132          $this->assertTrue($capabilitymanager->can_view_post_shell($this->user, $privatepostfrommetoother));
1133          $this->assertTrue($capabilitymanager->can_view_post_shell($this->user, $privatepostfromothertome));
1134          $this->assertFalse($capabilitymanager->can_view_post_shell($this->user, $privatepostfromothertoother));
1135  
1136          $this->give_capability('mod/forum:readprivatereplies');
1137          $this->assertTrue($capabilitymanager->can_view_post_shell($this->user, $post));
1138          $this->assertTrue($capabilitymanager->can_view_post_shell($this->user, $privatepostfrommetome));
1139          $this->assertTrue($capabilitymanager->can_view_post_shell($this->user, $privatepostfrommetoother));
1140          $this->assertTrue($capabilitymanager->can_view_post_shell($this->user, $privatepostfromothertome));
1141          $this->assertTrue($capabilitymanager->can_view_post_shell($this->user, $privatepostfromothertoother));
1142      }
1143  
1144      /**
1145       * Test can_export_post.
1146       *
1147       * @covers ::can_export_post
1148       */
1149      public function test_can_export_post() {
1150          global $CFG;
1151          $this->resetAfterTest();
1152  
1153          $forum = $this->create_forum();
1154          $post = $this->post;
1155          $user = $this->user;
1156          $otheruser = $this->getDataGenerator()->create_user();
1157          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
1158  
1159          $this->getDataGenerator()->enrol_user($otheruser->id, $this->course->id, 'teacher');
1160  
1161          $CFG->enableportfolios = true;
1162          $this->give_capability('mod/forum:exportpost');
1163  
1164          $this->assertTrue($capabilitymanager->can_export_post($otheruser, $post));
1165  
1166          $CFG->enableportfolios = false;
1167          $this->assertFalse($capabilitymanager->can_export_post($otheruser, $post));
1168  
1169          $CFG->enableportfolios = true;
1170          $this->prevent_capability('mod/forum:exportpost');
1171          // Can't export another user's post without the exportpost capavility.
1172          $this->assertFalse($capabilitymanager->can_export_post($otheruser, $post));
1173  
1174          $this->give_capability('mod/forum:exportownpost');
1175          // Can export own post with the exportownpost capability.
1176          $this->assertTrue($capabilitymanager->can_export_post($user, $post));
1177  
1178          $this->prevent_capability('mod/forum:exportownpost');
1179          $this->assertFalse($capabilitymanager->can_export_post($user, $post));
1180      }
1181  
1182      /**
1183       * Test can_view_participants.
1184       *
1185       * @covers ::can_view_participants
1186       */
1187      public function test_can_view_participants() {
1188          $this->resetAfterTest();
1189  
1190          $discussion = $this->discussion;
1191          $user = $this->user;
1192          $otheruser = $this->getDataGenerator()->create_user();
1193  
1194          $this->getDataGenerator()->enrol_user($otheruser->id, $this->course->id, 'teacher');
1195  
1196          $this->prevent_capability('moodle/course:viewparticipants');
1197          $this->prevent_capability('moodle/course:enrolreview');
1198          $this->prevent_capability('mod/forum:viewqandawithoutposting');
1199  
1200          $forum = $this->create_forum();
1201          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
1202  
1203          $this->assertFalse($capabilitymanager->can_view_participants($otheruser, $discussion));
1204  
1205          $this->give_capability('moodle/course:viewparticipants');
1206          $this->assertTrue($capabilitymanager->can_view_participants($otheruser, $discussion));
1207  
1208          $this->prevent_capability('moodle/course:viewparticipants');
1209          $this->give_capability('moodle/course:enrolreview');
1210          $this->assertTrue($capabilitymanager->can_view_participants($otheruser, $discussion));
1211  
1212          $forum = $this->create_forum(['type' => 'qanda']);
1213          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
1214  
1215          // Q and A forum requires the user to post before they can view it.
1216          $this->prevent_capability('mod/forum:viewqandawithoutposting');
1217          $this->assertFalse($capabilitymanager->can_view_participants($otheruser, $discussion));
1218  
1219          // This user has posted.
1220          $this->assertTrue($capabilitymanager->can_view_participants($user, $discussion));
1221      }
1222  
1223      /**
1224       * Test can_view_hidden_posts.
1225       *
1226       * @covers ::can_view_hidden_posts
1227       */
1228      public function test_can_view_hidden_posts() {
1229          $this->resetAfterTest();
1230  
1231          $forum = $this->create_forum();
1232          $user = $this->user;
1233          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
1234  
1235          $this->prevent_capability('mod/forum:viewhiddentimedposts');
1236          $this->assertFalse($capabilitymanager->can_view_hidden_posts($user));
1237  
1238          $this->give_capability('mod/forum:viewhiddentimedposts');
1239          $this->assertTrue($capabilitymanager->can_view_hidden_posts($user));
1240      }
1241  
1242      /**
1243       * Test can_manage_forum.
1244       *
1245       * @covers ::can_manage_forum
1246       */
1247      public function test_can_manage_forum() {
1248          $this->resetAfterTest();
1249  
1250          $forum = $this->create_forum();
1251          $user = $this->user;
1252          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
1253  
1254          $this->prevent_capability('moodle/course:manageactivities');
1255          $this->assertFalse($capabilitymanager->can_manage_forum($user));
1256  
1257          $this->give_capability('moodle/course:manageactivities');
1258          $this->assertTrue($capabilitymanager->can_manage_forum($user));
1259      }
1260  
1261      /**
1262       * Test can_manage_tags.
1263       *
1264       * @covers ::can_manage_tags
1265       */
1266      public function test_can_manage_tags() {
1267          global $DB;
1268          $this->resetAfterTest();
1269  
1270          $forum = $this->create_forum();
1271          $user = $this->user;
1272          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
1273          $context = \context_system::instance();
1274          $roleid = $DB->get_field('role', 'id', ['shortname' => 'user'], MUST_EXIST);
1275  
1276          assign_capability('moodle/tag:manage', CAP_PREVENT, $roleid, $context->id, true);
1277          $this->assertFalse($capabilitymanager->can_manage_tags($user));
1278  
1279          assign_capability('moodle/tag:manage', CAP_ALLOW, $roleid, $context->id, true);
1280          $this->assertTrue($capabilitymanager->can_manage_tags($user));
1281      }
1282  
1283      /**
1284       * Ensure that the can_view_any_private_reply works as expected.
1285       *
1286       * @covers ::can_view_any_private_reply
1287       */
1288      public function test_can_view_any_private_reply() {
1289          $this->resetAfterTest();
1290  
1291          $forum = $this->create_forum();
1292          $capabilitymanager = $this->managerfactory->get_capability_manager($forum);
1293  
1294          $this->give_capability('mod/forum:readprivatereplies');
1295          $this->assertTrue($capabilitymanager->can_view_any_private_reply($this->user));
1296          $this->prevent_capability('mod/forum:readprivatereplies');
1297          $this->assertFalse($capabilitymanager->can_view_any_private_reply($this->user));
1298      }
1299  
1300  
1301      /**
1302       * Test delete a post with ratings.
1303       */
1304      public function test_validate_delete_post_with_ratings() {
1305          global $DB;
1306          $this->resetAfterTest(true);
1307  
1308          // Setup test data.
1309          $course = $this->getDataGenerator()->create_course();
1310          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
1311          $user = $this->getDataGenerator()->create_user();
1312          $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1313          self::getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
1314  
1315          // Add a discussion.
1316          $record = new \stdClass();
1317          $record->course = $course->id;
1318          $record->userid = $user->id;
1319          $record->forum = $forum->id;
1320          $record->created =
1321          $discussion = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1322  
1323          // Add rating.
1324          $post = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
1325          $post->totalscore = 80;
1326          $DB->update_record('forum_posts', $post);
1327  
1328          $vaultfactory = container::get_vault_factory();
1329          $forumvault = $vaultfactory->get_forum_vault();
1330          $discussionvault = $vaultfactory->get_discussion_vault();
1331          $postvault = $vaultfactory->get_post_vault();
1332  
1333          $postentity = $postvault->get_from_id($post->id);
1334          $discussionentity = $discussionvault->get_from_id($postentity->get_discussion_id());
1335          $forumentity = $forumvault->get_from_id($discussionentity->get_forum_id());
1336          $capabilitymanager = $this->managerfactory->get_capability_manager($forumentity);
1337  
1338          $this->setUser($user);
1339          $this->expectExceptionMessage(get_string('couldnotdeleteratings', 'rating'));
1340          $capabilitymanager->validate_delete_post($user, $discussionentity, $postentity, false);
1341      }
1342  
1343      /**
1344       * Test delete a post with replies.
1345       */
1346      public function test_validate_delete_post_with_replies() {
1347          global $DB;
1348          $this->resetAfterTest(true);
1349  
1350          // Setup test data.
1351          $course = $this->getDataGenerator()->create_course();
1352          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
1353          $user = $this->getDataGenerator()->create_user();
1354          $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1355          self::getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
1356  
1357          // Add a discussion.
1358          $record = new \stdClass();
1359          $record->course = $course->id;
1360          $record->userid = $user->id;
1361          $record->forum = $forum->id;
1362          $record->created =
1363          $discussion = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1364  
1365          $parentpost = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
1366          // Add a post.
1367          $record = new \stdClass();
1368          $record->course = $course->id;
1369          $record->userid = $user->id;
1370          $record->forum = $forum->id;
1371          $record->discussion = $discussion->id;
1372          $record->parent = $parentpost->id;
1373          $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1374  
1375          $vaultfactory = container::get_vault_factory();
1376          $forumvault = $vaultfactory->get_forum_vault();
1377          $discussionvault = $vaultfactory->get_discussion_vault();
1378          $postvault = $vaultfactory->get_post_vault();
1379  
1380          $postentity = $postvault->get_from_id($parentpost->id);
1381          $discussionentity = $discussionvault->get_from_id($postentity->get_discussion_id());
1382          $forumentity = $forumvault->get_from_id($discussionentity->get_forum_id());
1383          $capabilitymanager = $this->managerfactory->get_capability_manager($forumentity);
1384  
1385          $this->setUser($user);
1386          // Get reply count.
1387          $replycount = $postvault->get_reply_count_for_post_id_in_discussion_id(
1388              $user, $postentity->get_id(), $discussionentity->get_id(), true);
1389          $this->expectExceptionMessage(get_string('couldnotdeletereplies', 'forum'));
1390          $capabilitymanager->validate_delete_post($user, $discussionentity, $postentity, $replycount);
1391      }
1392  
1393  }