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  namespace tool_policy;
  18  
  19  use tool_policy\test\helper;
  20  
  21  /**
  22   * Unit tests for the {@link \tool_policy\api} class.
  23   *
  24   * @package   tool_policy
  25   * @category  test
  26   * @copyright 2018 David Mudrak <david@moodle.com>
  27   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  28   */
  29  class api_test extends \advanced_testcase {
  30  
  31      /**
  32       * Test the common operations with a policy document and its versions.
  33       */
  34      public function test_policy_document_life_cycle() {
  35          $this->resetAfterTest();
  36          $this->setAdminUser();
  37  
  38          // Prepare the form data for adding a new policy document.
  39          $formdata = api::form_policydoc_data(new policy_version(0));
  40          $this->assertObjectHasAttribute('name', $formdata);
  41          $this->assertArrayHasKey('text', $formdata->summary_editor);
  42          $this->assertArrayHasKey('format', $formdata->content_editor);
  43  
  44          // Save the form.
  45          $formdata->name = 'Test terms & conditions';
  46          $formdata->type = policy_version::TYPE_OTHER;
  47          $policy = api::form_policydoc_add($formdata);
  48          $record = $policy->to_record();
  49  
  50          $this->assertNotEmpty($record->id);
  51          $this->assertNotEmpty($record->policyid);
  52          $this->assertNotEmpty($record->timecreated);
  53          $this->assertNotEmpty($record->timemodified);
  54          $this->assertNotNull($record->name);
  55          $this->assertNotNull($record->summary);
  56          $this->assertNotNull($record->summaryformat);
  57          $this->assertNotNull($record->content);
  58          $this->assertNotNull($record->contentformat);
  59  
  60          // Update the policy document version.
  61          $formdata = api::form_policydoc_data($policy);
  62          $formdata->revision = '*** Unit test ***';
  63          $formdata->summary_editor['text'] = '__Just a summary__';
  64          $formdata->summary_editor['format'] = FORMAT_MARKDOWN;
  65          $formdata->content_editor['text'] = '### Just a test ###';
  66          $formdata->content_editor['format'] = FORMAT_MARKDOWN;
  67          $updated = api::form_policydoc_update_overwrite($formdata);
  68          $this->assertEquals($policy->get('id'), $updated->get('id'));
  69          $this->assertEquals($policy->get('policyid'), $updated->get('policyid'));
  70  
  71          // Save form as a new version.
  72          $formdata = api::form_policydoc_data($policy);
  73          $formdata->name = 'New terms & conditions';
  74          $formdata->revision = '*** Unit test 2 ***';
  75          $formdata->summary_editor['text'] = '<strong>Yet another summary</strong>';
  76          $formdata->summary_editor['format'] = FORMAT_MOODLE;
  77          $formdata->content_editor['text'] = '<h3>Yet another test</h3>';
  78          $formdata->content_editor['format'] = FORMAT_HTML;
  79          $new = api::form_policydoc_update_new($formdata);
  80          $this->assertNotEquals($policy->get('id'), $new->get('id'));
  81          $this->assertEquals($policy->get('policyid'), $new->get('policyid'));
  82  
  83          // Add yet another policy document.
  84          $formdata = api::form_policydoc_data(new policy_version(0));
  85          $formdata->name = 'Privacy terms';
  86          $formdata->type = policy_version::TYPE_PRIVACY;
  87          $another = api::form_policydoc_add($formdata);
  88  
  89          // Get the list of all policies and their versions.
  90          $docs = api::list_policies();
  91          $this->assertEquals(2, count($docs));
  92  
  93          // Get just one policy and all its versions.
  94          $docs = api::list_policies($another->get('policyid'));
  95          $this->assertEquals(1, count($docs));
  96  
  97          // Activate a policy.
  98          $this->assertEquals(0, count(api::list_current_versions()));
  99          api::make_current($updated->get('id'));
 100          $current = api::list_current_versions();
 101          $this->assertEquals(1, count($current));
 102          $first = reset($current);
 103          $this->assertEquals('Test terms &amp; conditions', $first->name);
 104  
 105          // Activate another policy version.
 106          api::make_current($new->get('id'));
 107          $current = api::list_current_versions();
 108          $this->assertEquals(1, count($current));
 109          $first = reset($current);
 110          $this->assertEquals('New terms &amp; conditions', $first->name);
 111  
 112          // Inactivate the policy.
 113          api::inactivate($new->get('policyid'));
 114          $this->assertEmpty(api::list_current_versions());
 115          $archived = api::get_policy_version($new->get('id'));
 116          $this->assertEquals(policy_version::STATUS_ARCHIVED, $archived->status);
 117  
 118          // Create a new draft from an archived version.
 119          $draft = api::revert_to_draft($archived->id);
 120          $draft = api::get_policy_version($draft->get('id'));
 121          $archived = api::get_policy_version($archived->id);
 122          $this->assertEmpty(api::list_current_versions());
 123          $this->assertNotEquals($draft->id, $archived->id);
 124          $this->assertEquals(policy_version::STATUS_DRAFT, $draft->status);
 125          $this->assertEquals(policy_version::STATUS_ARCHIVED, $archived->status);
 126  
 127          // An active policy can't be set to draft.
 128          api::make_current($draft->id);
 129          $this->expectException('coding_exception');
 130          $this->expectExceptionMessage('Version not found or is not archived');
 131          api::revert_to_draft($draft->id);
 132      }
 133  
 134      /**
 135       * Test changing the sort order of the policy documents.
 136       */
 137      public function test_policy_sortorder() {
 138          global $DB;
 139          $this->resetAfterTest();
 140          $this->setAdminUser();
 141  
 142          $formdata = api::form_policydoc_data(new policy_version(0));
 143          $formdata->name = 'Policy1';
 144          $formdata->summary_editor = ['text' => 'P1 summary', 'format' => FORMAT_HTML, 'itemid' => 0];
 145          $formdata->content_editor = ['text' => 'P1 content', 'format' => FORMAT_HTML, 'itemid' => 0];
 146          $policy1 = api::form_policydoc_add($formdata);
 147          $policy1sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy1->get('policyid')]);
 148  
 149          $formdata = api::form_policydoc_data(new policy_version(0));
 150          $formdata->name = 'Policy2';
 151          $formdata->summary_editor = ['text' => 'P2 summary', 'format' => FORMAT_HTML, 'itemid' => 0];
 152          $formdata->content_editor = ['text' => 'P2 content', 'format' => FORMAT_HTML, 'itemid' => 0];
 153          $policy2 = api::form_policydoc_add($formdata);
 154          $policy2sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy2->get('policyid')]);
 155  
 156          $this->assertTrue($policy1sortorder < $policy2sortorder);
 157  
 158          $formdata = api::form_policydoc_data(new policy_version(0));
 159          $formdata->name = 'Policy3';
 160          $formdata->summary_editor = ['text' => 'P3 summary', 'format' => FORMAT_HTML, 'itemid' => 0];
 161          $formdata->content_editor = ['text' => 'P3 content', 'format' => FORMAT_HTML, 'itemid' => 0];
 162          $policy3 = api::form_policydoc_add($formdata);
 163          $policy3sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy3->get('policyid')]);
 164  
 165          $this->assertTrue($policy1sortorder < $policy2sortorder);
 166          $this->assertTrue($policy2sortorder < $policy3sortorder);
 167  
 168          api::move_up($policy3->get('policyid'));
 169  
 170          $policy1sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy1->get('policyid')]);
 171          $policy2sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy2->get('policyid')]);
 172          $policy3sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy3->get('policyid')]);
 173  
 174          $this->assertTrue($policy1sortorder < $policy3sortorder);
 175          $this->assertTrue($policy3sortorder < $policy2sortorder);
 176  
 177          api::move_down($policy1->get('policyid'));
 178  
 179          $policy1sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy1->get('policyid')]);
 180          $policy2sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy2->get('policyid')]);
 181          $policy3sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy3->get('policyid')]);
 182  
 183          $this->assertTrue($policy3sortorder < $policy1sortorder);
 184          $this->assertTrue($policy1sortorder < $policy2sortorder);
 185  
 186          $orderedlist = [];
 187          foreach (api::list_policies() as $policy) {
 188              $orderedlist[] = $policy->id;
 189          }
 190          $this->assertEquals([$policy3->get('policyid'), $policy1->get('policyid'), $policy2->get('policyid')], $orderedlist);
 191      }
 192  
 193      /**
 194       * Test that list of policies can be filtered by audience
 195       */
 196      public function test_list_policies_audience() {
 197          $this->resetAfterTest();
 198          $this->setAdminUser();
 199  
 200          $policy1 = helper::add_policy(['audience' => policy_version::AUDIENCE_LOGGEDIN]);
 201          $policy2 = helper::add_policy(['audience' => policy_version::AUDIENCE_GUESTS]);
 202          $policy3 = helper::add_policy();
 203  
 204          api::make_current($policy1->get('id'));
 205          api::make_current($policy2->get('id'));
 206          api::make_current($policy3->get('id'));
 207  
 208          $list = array_map(function ($version) {
 209              return $version->policyid;
 210          }, api::list_current_versions());
 211          $this->assertEquals([$policy1->get('policyid'), $policy2->get('policyid'), $policy3->get('policyid')],
 212              array_values($list));
 213          $ids = api::get_current_versions_ids();
 214          $this->assertEquals([$policy1->get('policyid') => $policy1->get('id'),
 215              $policy2->get('policyid') => $policy2->get('id'),
 216              $policy3->get('policyid') => $policy3->get('id')], $ids);
 217  
 218          $list = array_map(function ($version) {
 219              return $version->policyid;
 220          }, api::list_current_versions(policy_version::AUDIENCE_LOGGEDIN));
 221          $this->assertEquals([$policy1->get('policyid'), $policy3->get('policyid')], array_values($list));
 222          $ids = api::get_current_versions_ids(policy_version::AUDIENCE_LOGGEDIN);
 223          $this->assertEquals([$policy1->get('policyid') => $policy1->get('id'),
 224              $policy3->get('policyid') => $policy3->get('id')], $ids);
 225  
 226          $list = array_map(function ($version) {
 227              return $version->policyid;
 228          }, api::list_current_versions(policy_version::AUDIENCE_GUESTS));
 229          $this->assertEquals([$policy2->get('policyid'), $policy3->get('policyid')], array_values($list));
 230          $ids = api::get_current_versions_ids(policy_version::AUDIENCE_GUESTS);
 231          $this->assertEquals([$policy2->get('policyid') => $policy2->get('id'),
 232              $policy3->get('policyid') => $policy3->get('id')], $ids);
 233      }
 234  
 235      /**
 236       * Test behaviour of the {@link api::can_user_view_policy_version()} method.
 237       */
 238      public function test_can_user_view_policy_version() {
 239          global $CFG;
 240          $this->resetAfterTest();
 241          $this->setAdminUser();
 242  
 243          $child = $this->getDataGenerator()->create_user();
 244          $parent = $this->getDataGenerator()->create_user();
 245          $this->getDataGenerator()->create_user();
 246          $officer = $this->getDataGenerator()->create_user();
 247          $manager = $this->getDataGenerator()->create_user();
 248  
 249          $syscontext = \context_system::instance();
 250          $childcontext = \context_user::instance($child->id);
 251  
 252          $roleminorid = create_role('Digital minor', 'digiminor', 'Not old enough to accept site policies themselves');
 253          $roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
 254          $roleofficerid = create_role('Policy officer', 'policyofficer', 'Can see all acceptances but can\'t edit policy documents');
 255          $rolemanagerid = create_role('Policy manager', 'policymanager', 'Can manage policy documents');
 256  
 257          assign_capability('tool/policy:accept', CAP_PROHIBIT, $roleminorid, $syscontext->id);
 258          assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
 259          assign_capability('tool/policy:viewacceptances', CAP_ALLOW, $roleofficerid, $syscontext->id);
 260          assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $syscontext->id);
 261  
 262          role_assign($roleminorid, $child->id, $syscontext->id);
 263          // Becoming a parent is easy. Being a good one is difficult.
 264          role_assign($roleparentid, $parent->id, $childcontext->id);
 265          role_assign($roleofficerid, $officer->id, $syscontext->id);
 266          role_assign($rolemanagerid, $manager->id, $syscontext->id);
 267  
 268          accesslib_clear_all_caches_for_unit_testing();
 269  
 270          // Prepare a policy document with some versions.
 271          list($policy1, $policy2, $policy3) = helper::create_versions(3);
 272  
 273          // Normally users do not have access to policy drafts.
 274          $this->assertFalse(api::can_user_view_policy_version($policy1, null, $child->id));
 275          $this->assertFalse(api::can_user_view_policy_version($policy2, null, $parent->id));
 276          $this->assertFalse(api::can_user_view_policy_version($policy3, null, $CFG->siteguest));
 277  
 278          // Officers and managers have access even to drafts.
 279          $this->assertTrue(api::can_user_view_policy_version($policy1, null, $officer->id));
 280          $this->assertTrue(api::can_user_view_policy_version($policy3, null, $manager->id));
 281  
 282          // Current versions are public so that users can decide whether to even register on such a site.
 283          api::make_current($policy2->id);
 284          $policy1 = api::get_policy_version($policy1->id);
 285          $policy2 = api::get_policy_version($policy2->id);
 286          $policy3 = api::get_policy_version($policy3->id);
 287  
 288          $this->assertFalse(api::can_user_view_policy_version($policy1, null, $child->id));
 289          $this->assertTrue(api::can_user_view_policy_version($policy2, null, $child->id));
 290          $this->assertTrue(api::can_user_view_policy_version($policy2, null, $CFG->siteguest));
 291          $this->assertFalse(api::can_user_view_policy_version($policy3, null, $child->id));
 292  
 293          // Let the parent accept the policy on behalf of her child.
 294          $this->setUser($parent);
 295          api::accept_policies($policy2->id, $child->id);
 296  
 297          // Release a new version of the policy.
 298          api::make_current($policy3->id);
 299          $policy1 = api::get_policy_version($policy1->id);
 300          $policy2 = api::get_policy_version($policy2->id);
 301          $policy3 = api::get_policy_version($policy3->id);
 302  
 303          api::get_user_minors($parent->id);
 304          // They should now have access to the archived version (because they agreed) and the current one.
 305          $this->assertFalse(api::can_user_view_policy_version($policy1, null, $child->id));
 306          $this->assertFalse(api::can_user_view_policy_version($policy1, null, $parent->id));
 307          $this->assertTrue(api::can_user_view_policy_version($policy2, null, $child->id));
 308          $this->assertTrue(api::can_user_view_policy_version($policy2, null, $parent->id));
 309          $this->assertTrue(api::can_user_view_policy_version($policy3, null, $child->id));
 310          $this->assertTrue(api::can_user_view_policy_version($policy3, null, $parent->id));
 311      }
 312  
 313      /**
 314       * Test behaviour of the {@link api::can_accept_policies()} method.
 315       */
 316      public function test_can_accept_policies() {
 317          global $CFG;
 318  
 319          $this->resetAfterTest();
 320          $this->setAdminUser();
 321  
 322          $user = $this->getDataGenerator()->create_user();
 323          $child = $this->getDataGenerator()->create_user();
 324          $parent = $this->getDataGenerator()->create_user();
 325          $officer = $this->getDataGenerator()->create_user();
 326          $manager = $this->getDataGenerator()->create_user();
 327  
 328          $syscontext = \context_system::instance();
 329          $childcontext = \context_user::instance($child->id);
 330  
 331          $roleminorid = create_role('Digital minor', 'digiminor', 'Not old enough to accept site policies themselves');
 332          $roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
 333          $roleofficerid = create_role('Policy officer', 'policyofficer', 'Can see all acceptances but can\'t edit policy documents');
 334          $rolemanagerid = create_role('Policy manager', 'policymanager', 'Can manage policy documents');
 335  
 336          assign_capability('tool/policy:accept', CAP_PROHIBIT, $roleminorid, $syscontext->id);
 337          assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
 338          assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleofficerid, $syscontext->id);
 339          assign_capability('tool/policy:viewacceptances', CAP_ALLOW, $roleofficerid, $syscontext->id);
 340          assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $rolemanagerid, $syscontext->id);
 341          assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $syscontext->id);
 342  
 343          role_assign($roleminorid, $child->id, $syscontext->id);
 344          role_assign($roleparentid, $parent->id, $childcontext->id);
 345          role_assign($roleofficerid, $officer->id, $syscontext->id);
 346          role_assign($rolemanagerid, $manager->id, $syscontext->id);
 347  
 348          accesslib_clear_all_caches_for_unit_testing();
 349  
 350          $policy1 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
 351          $policy2 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
 352          $policy3 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
 353          $policy4 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
 354  
 355          $mixed = [$policy1->id, $policy2->id, $policy3->id, $policy4->id];
 356          $compulsory = [$policy1->id, $policy2->id];
 357          $optional = [$policy3->id, $policy4->id];
 358  
 359          // Normally users can accept all policies.
 360          $this->setUser($user);
 361          $this->assertTrue(api::can_accept_policies($mixed));
 362          $this->assertTrue(api::can_accept_policies($compulsory));
 363          $this->assertTrue(api::can_accept_policies($optional));
 364  
 365          // Digital minors can be set to not be able to accept policies themselves.
 366          $this->setUser($child);
 367          $this->assertFalse(api::can_accept_policies($mixed));
 368          $this->assertFalse(api::can_accept_policies($compulsory));
 369          $this->assertFalse(api::can_accept_policies($optional));
 370  
 371          // The parent can accept optional policies on child's behalf.
 372          $this->setUser($parent);
 373          $this->assertTrue(api::can_accept_policies($mixed, $child->id));
 374          $this->assertTrue(api::can_accept_policies($compulsory, $child->id));
 375          $this->assertTrue(api::can_accept_policies($optional, $child->id));
 376  
 377          // Officers and managers can accept on other user's behalf.
 378          $this->setUser($officer);
 379          $this->assertTrue(api::can_accept_policies($mixed, $parent->id));
 380          $this->assertTrue(api::can_accept_policies($compulsory, $parent->id));
 381          $this->assertTrue(api::can_accept_policies($optional, $parent->id));
 382  
 383          $this->setUser($manager);
 384          $this->assertTrue(api::can_accept_policies($mixed, $parent->id));
 385          $this->assertTrue(api::can_accept_policies($compulsory, $parent->id));
 386          $this->assertTrue(api::can_accept_policies($optional, $parent->id));
 387      }
 388  
 389      /**
 390       * Test behaviour of the {@link api::can_decline_policies()} method.
 391       */
 392      public function test_can_decline_policies() {
 393          global $CFG;
 394  
 395          $this->resetAfterTest();
 396          $this->setAdminUser();
 397  
 398          $user = $this->getDataGenerator()->create_user();
 399          $child = $this->getDataGenerator()->create_user();
 400          $parent = $this->getDataGenerator()->create_user();
 401          $officer = $this->getDataGenerator()->create_user();
 402          $manager = $this->getDataGenerator()->create_user();
 403  
 404          $syscontext = \context_system::instance();
 405          $childcontext = \context_user::instance($child->id);
 406  
 407          $roleminorid = create_role('Digital minor', 'digiminor', 'Not old enough to accept site policies themselves');
 408          $roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
 409          $roleofficerid = create_role('Policy officer', 'policyofficer', 'Can see all acceptances but can\'t edit policy documents');
 410          $rolemanagerid = create_role('Policy manager', 'policymanager', 'Can manage policy documents');
 411  
 412          assign_capability('tool/policy:accept', CAP_PROHIBIT, $roleminorid, $syscontext->id);
 413          assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
 414          assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleofficerid, $syscontext->id);
 415          assign_capability('tool/policy:viewacceptances', CAP_ALLOW, $roleofficerid, $syscontext->id);
 416          assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $rolemanagerid, $syscontext->id);
 417          assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $syscontext->id);
 418  
 419          role_assign($roleminorid, $child->id, $syscontext->id);
 420          role_assign($roleparentid, $parent->id, $childcontext->id);
 421          role_assign($roleofficerid, $officer->id, $syscontext->id);
 422          role_assign($rolemanagerid, $manager->id, $syscontext->id);
 423  
 424          accesslib_clear_all_caches_for_unit_testing();
 425  
 426          $policy1 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
 427          $policy2 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
 428          $policy3 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
 429          $policy4 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
 430  
 431          $mixed = [$policy1->id, $policy2->id, $policy3->id, $policy4->id];
 432          $compulsory = [$policy1->id, $policy2->id];
 433          $optional = [$policy3->id, $policy4->id];
 434  
 435          // Normally users can decline only optional policies.
 436          $this->setUser($user);
 437          $this->assertFalse(api::can_decline_policies($mixed));
 438          $this->assertFalse(api::can_decline_policies($compulsory));
 439          $this->assertTrue(api::can_decline_policies($optional));
 440  
 441          // If they can't accept them, they can't decline them too.
 442          $this->setUser($child);
 443          $this->assertFalse(api::can_decline_policies($mixed));
 444          $this->assertFalse(api::can_decline_policies($compulsory));
 445          $this->assertFalse(api::can_decline_policies($optional));
 446  
 447          // The parent can decline optional policies on child's behalf.
 448          $this->setUser($parent);
 449          $this->assertFalse(api::can_decline_policies($mixed, $child->id));
 450          $this->assertFalse(api::can_decline_policies($compulsory, $child->id));
 451          $this->assertTrue(api::can_decline_policies($optional, $child->id));
 452  
 453          // Even officers or managers cannot decline compulsory policies.
 454          $this->setUser($officer);
 455          $this->assertFalse(api::can_decline_policies($mixed));
 456          $this->assertFalse(api::can_decline_policies($compulsory));
 457          $this->assertTrue(api::can_decline_policies($optional));
 458          $this->assertFalse(api::can_decline_policies($mixed, $child->id));
 459          $this->assertFalse(api::can_decline_policies($compulsory, $child->id));
 460          $this->assertTrue(api::can_decline_policies($optional, $child->id));
 461  
 462          $this->setUser($manager);
 463          $this->assertFalse(api::can_decline_policies($mixed));
 464          $this->assertFalse(api::can_decline_policies($compulsory));
 465          $this->assertTrue(api::can_decline_policies($optional));
 466          $this->assertFalse(api::can_decline_policies($mixed, $child->id));
 467          $this->assertFalse(api::can_decline_policies($compulsory, $child->id));
 468          $this->assertTrue(api::can_decline_policies($optional, $child->id));
 469      }
 470  
 471      /**
 472       * Test behaviour of the {@link api::can_revoke_policies()} method.
 473       */
 474      public function test_can_revoke_policies() {
 475          global $CFG;
 476  
 477          $this->resetAfterTest();
 478          $this->setAdminUser();
 479  
 480          $user = $this->getDataGenerator()->create_user();
 481          $child = $this->getDataGenerator()->create_user();
 482          $parent = $this->getDataGenerator()->create_user();
 483          $officer = $this->getDataGenerator()->create_user();
 484          $manager = $this->getDataGenerator()->create_user();
 485  
 486          $syscontext = \context_system::instance();
 487          $childcontext = \context_user::instance($child->id);
 488  
 489          $roleminorid = create_role('Digital minor', 'digiminor', 'Not old enough to accept site policies themselves');
 490          $roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
 491          $roleofficerid = create_role('Policy officer', 'policyofficer', 'Can see all acceptances but can\'t edit policy documents');
 492          $rolemanagerid = create_role('Policy manager', 'policymanager', 'Can manage policy documents');
 493  
 494          assign_capability('tool/policy:accept', CAP_PROHIBIT, $roleminorid, $syscontext->id);
 495          assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
 496          assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleofficerid, $syscontext->id);
 497          assign_capability('tool/policy:viewacceptances', CAP_ALLOW, $roleofficerid, $syscontext->id);
 498          assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $rolemanagerid, $syscontext->id);
 499          assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $syscontext->id);
 500  
 501          role_assign($roleminorid, $child->id, $syscontext->id);
 502          // Becoming a parent is easy. Being a good one is difficult.
 503          role_assign($roleparentid, $parent->id, $childcontext->id);
 504          role_assign($roleofficerid, $officer->id, $syscontext->id);
 505          role_assign($rolemanagerid, $manager->id, $syscontext->id);
 506  
 507          accesslib_clear_all_caches_for_unit_testing();
 508  
 509          $policy1 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
 510          $policy2 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
 511  
 512          $versionids = [$policy1->id, $policy2->id];
 513  
 514          // Guests cannot revoke anything.
 515          $this->setGuestUser();
 516          $this->assertFalse(api::can_revoke_policies($versionids));
 517  
 518          // Normally users do not have access to revoke policies.
 519          $this->setUser($user);
 520          $this->assertFalse(api::can_revoke_policies($versionids, $user->id));
 521          $this->setUser($child);
 522          $this->assertFalse(api::can_revoke_policies($versionids, $child->id));
 523  
 524          // Optional policies can be revoked if the user can accept them.
 525          $this->setUser($user);
 526          $this->assertTrue(api::can_revoke_policies([$policy2->id]));
 527          $this->assertTrue(api::can_revoke_policies([$policy2->id], $user->id));
 528          $this->setUser($child);
 529          $this->assertFalse(api::can_revoke_policies([$policy2->id]));
 530          $this->assertFalse(api::can_revoke_policies([$policy2->id], $child->id));
 531  
 532          // The parent can revoke the policy on behalf of her child (but not her own policies, unless they are optional).
 533          $this->setUser($parent);
 534          $this->assertFalse(api::can_revoke_policies($versionids, $parent->id));
 535          $this->assertTrue(api::can_revoke_policies($versionids, $child->id));
 536          $this->assertTrue(api::can_revoke_policies([$policy2->id]));
 537          $this->assertTrue(api::can_revoke_policies([$policy2->id], $child->id));
 538  
 539          // Officers and managers can revoke everything.
 540          $this->setUser($officer);
 541          $this->assertTrue(api::can_revoke_policies($versionids, $officer->id));
 542          $this->assertTrue(api::can_revoke_policies($versionids, $child->id));
 543          $this->assertTrue(api::can_revoke_policies($versionids, $parent->id));
 544          $this->assertTrue(api::can_revoke_policies($versionids, $manager->id));
 545  
 546          $this->setUser($manager);
 547          $this->assertTrue(api::can_revoke_policies($versionids, $manager->id));
 548          $this->assertTrue(api::can_revoke_policies($versionids, $child->id));
 549          $this->assertTrue(api::can_revoke_policies($versionids, $parent->id));
 550          $this->assertTrue(api::can_revoke_policies($versionids, $officer->id));
 551      }
 552  
 553      /**
 554       * Test {@link api::fix_revision_values()} behaviour.
 555       */
 556      public function test_fix_revision_values() {
 557          $this->resetAfterTest();
 558          $this->setAdminUser();
 559  
 560          $versions = [
 561              (object) ['id' => 80, 'timecreated' => mktime(1, 1, 1, 12, 28, 2018), 'revision' => '', 'e' => '28 December 2018'],
 562              (object) ['id' => 70, 'timecreated' => mktime(1, 1, 1, 12, 27, 2018), 'revision' => '', 'e' => '27 December 2018 - v2'],
 563              (object) ['id' => 60, 'timecreated' => mktime(1, 1, 1, 12, 27, 2018), 'revision' => '', 'e' => '27 December 2018 - v1'],
 564              (object) ['id' => 50, 'timecreated' => mktime(0, 0, 0, 12, 26, 2018), 'revision' => '0', 'e' => '0'],
 565              (object) ['id' => 40, 'timecreated' => mktime(0, 0, 0, 12, 26, 2018), 'revision' => '1.1', 'e' => '1.1 - v2'],
 566              (object) ['id' => 30, 'timecreated' => mktime(0, 0, 0, 12, 26, 2018), 'revision' => '1.1', 'e' => '1.1 - v1'],
 567              (object) ['id' => 20, 'timecreated' => mktime(0, 0, 0, 12, 26, 2018), 'revision' => '', 'e' => '26 December 2018'],
 568              (object) ['id' => 10, 'timecreated' => mktime(17, 57, 00, 12, 25, 2018), 'revision' => '1.0', 'e' => '1.0'],
 569          ];
 570  
 571          api::fix_revision_values($versions);
 572  
 573          foreach ($versions as $version) {
 574              $this->assertSame($version->revision, $version->e);
 575          }
 576      }
 577  
 578      /**
 579       * Test that accepting policy updates 'policyagreed'
 580       */
 581      public function test_accept_policies() {
 582          global $DB;
 583          $this->resetAfterTest();
 584          $this->setAdminUser();
 585  
 586          $policy1 = helper::add_policy()->to_record();
 587          api::make_current($policy1->id);
 588          $policy2 = helper::add_policy()->to_record();
 589          api::make_current($policy2->id);
 590          $policy3 = helper::add_policy(['optional' => true])->to_record();
 591          api::make_current($policy3->id);
 592  
 593          // Accept policy on behalf of somebody else.
 594          $user1 = $this->getDataGenerator()->create_user();
 595          $this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 596  
 597          // Accepting just compulsory policies is not enough, we want to hear explicitly about the optional one, too.
 598          api::accept_policies([$policy1->id, $policy2->id], $user1->id);
 599          $this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 600  
 601          // Optional policy does not need to be accepted, but it must be answered explicitly.
 602          api::decline_policies([$policy3->id], $user1->id);
 603          $this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 604  
 605          // Revoke previous agreement to a compulsory policy.
 606          api::revoke_acceptance($policy1->id, $user1->id);
 607          $this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 608  
 609          // Accept policies for oneself.
 610          $user2 = $this->getDataGenerator()->create_user();
 611          $this->setUser($user2);
 612  
 613          $this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user2->id]));
 614  
 615          api::accept_policies([$policy1->id]);
 616          $this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user2->id]));
 617  
 618          api::accept_policies([$policy2->id]);
 619          $this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user2->id]));
 620  
 621          api::decline_policies([$policy3->id]);
 622          $this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user2->id]));
 623  
 624          api::accept_policies([$policy3->id]);
 625          $this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user2->id]));
 626      }
 627  
 628      /**
 629       * Test that activating a new policy resets everybody's policyagreed flag in the database.
 630       */
 631      public function test_reset_policyagreed() {
 632          global $DB;
 633          $this->resetAfterTest();
 634          $this->setAdminUser();
 635  
 636          $user1 = $this->getDataGenerator()->create_user();
 637  
 638          // Introducing a new policy.
 639          list($policy1v1, $policy1v2) = helper::create_versions(2);
 640          api::make_current($policy1v1->id);
 641          $this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 642          api::accept_policies([$policy1v1->id], $user1->id);
 643          $this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 644  
 645          // Introducing another policy.
 646          $policy2v1 = helper::add_policy()->to_record();
 647          api::make_current($policy2v1->id);
 648          $this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 649          api::accept_policies([$policy2v1->id], $user1->id);
 650          $this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 651  
 652          // Updating an existing policy (major update).
 653          api::make_current($policy1v2->id);
 654          $this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 655          api::accept_policies([$policy1v2->id], $user1->id);
 656          $this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 657  
 658          // Do not touch the flag if there is no new version (e.g. a minor update).
 659          api::make_current($policy2v1->id);
 660          api::make_current($policy1v2->id);
 661          $this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 662  
 663          // Do not touch the flag if inactivating a policy.
 664          api::inactivate($policy1v2->policyid);
 665          $this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 666  
 667          // Do not touch the flag if setting to draft a policy.
 668          api::revert_to_draft($policy1v2->id);
 669          $this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
 670      }
 671  
 672      /**
 673       * Test behaviour of the {@link api::get_user_minors()} method.
 674       */
 675      public function test_get_user_minors() {
 676          $this->resetAfterTest();
 677  
 678          // A mother having two children, each child having own father.
 679          $mother1 = $this->getDataGenerator()->create_user();
 680          $father1 = $this->getDataGenerator()->create_user();
 681          $father2 = $this->getDataGenerator()->create_user();
 682          $child1 = $this->getDataGenerator()->create_user();
 683          $child2 = $this->getDataGenerator()->create_user();
 684  
 685          $syscontext = \context_system::instance();
 686          $child1context = \context_user::instance($child1->id);
 687          $child2context = \context_user::instance($child2->id);
 688  
 689          $roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
 690  
 691          assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
 692  
 693          role_assign($roleparentid, $mother1->id, $child1context->id);
 694          role_assign($roleparentid, $mother1->id, $child2context->id);
 695          role_assign($roleparentid, $father1->id, $child1context->id);
 696          role_assign($roleparentid, $father2->id, $child2context->id);
 697  
 698          accesslib_clear_all_caches_for_unit_testing();
 699  
 700          $mother1minors = api::get_user_minors($mother1->id);
 701          $this->assertEquals(2, count($mother1minors));
 702  
 703          $father1minors = api::get_user_minors($father1->id);
 704          $this->assertEquals(1, count($father1minors));
 705          $this->assertEquals($child1->id, $father1minors[$child1->id]->id);
 706  
 707          $father2minors = api::get_user_minors($father2->id);
 708          $this->assertEquals(1, count($father2minors));
 709          $this->assertEquals($child2->id, $father2minors[$child2->id]->id);
 710  
 711          $this->assertEmpty(api::get_user_minors($child1->id));
 712          $this->assertEmpty(api::get_user_minors($child2->id));
 713  
 714          $extradata = api::get_user_minors($mother1->id, ['policyagreed', 'deleted']);
 715          $this->assertTrue(property_exists($extradata[$child1->id], 'policyagreed'));
 716          $this->assertTrue(property_exists($extradata[$child1->id], 'deleted'));
 717          $this->assertTrue(property_exists($extradata[$child2->id], 'policyagreed'));
 718          $this->assertTrue(property_exists($extradata[$child2->id], 'deleted'));
 719      }
 720  
 721      /**
 722       * Test behaviour of the {@link api::create_acceptances_user_created()} method.
 723       */
 724      public function test_create_acceptances_user_created() {
 725          global $CFG, $DB;
 726          $this->resetAfterTest();
 727          $this->setAdminUser();
 728  
 729          $CFG->sitepolicyhandler = 'tool_policy';
 730  
 731          $policy = helper::add_policy()->to_record();
 732          api::make_current($policy->id);
 733  
 734          // User has not accepted any policies.
 735          $user1 = $this->getDataGenerator()->create_user();
 736          \core\event\user_created::create_from_userid($user1->id)->trigger();
 737  
 738          $this->assertEquals(0, $DB->count_records('tool_policy_acceptances',
 739              ['userid' => $user1->id, 'policyversionid' => $policy->id]));
 740  
 741          // User has accepted policies.
 742          $user2 = $this->getDataGenerator()->create_user();
 743          $DB->set_field('user', 'policyagreed', 1, ['id' => $user2->id]);
 744          \core\event\user_created::create_from_userid($user2->id)->trigger();
 745  
 746          $this->assertEquals(1, $DB->count_records('tool_policy_acceptances',
 747              ['userid' => $user2->id, 'policyversionid' => $policy->id]));
 748      }
 749  
 750      /**
 751       * Test that user can login if sitepolicyhandler is set but there are no policies.
 752       */
 753      public function test_login_with_handler_without_policies() {
 754          global $CFG;
 755  
 756          $this->resetAfterTest();
 757          $user = $this->getDataGenerator()->create_user();
 758          $this->setUser($user);
 759  
 760          $CFG->sitepolicyhandler = 'tool_policy';
 761  
 762          require_login(null, false, null, false, true);
 763      }
 764  
 765      /**
 766       * Test the three-state logic of the value returned by {@link api::is_user_version_accepted()}.
 767       */
 768      public function test_is_user_version_accepted() {
 769  
 770          $preloadedacceptances = [
 771              4 => (object) [
 772                  'policyversionid' => 4,
 773                  'mainuserid' => 13,
 774                  'status' => 1,
 775              ],
 776              6 => (object) [
 777                  'policyversionid' => 6,
 778                  'mainuserid' => 13,
 779                  'status' => 0,
 780              ],
 781          ];
 782  
 783          $this->assertTrue(api::is_user_version_accepted(13, 4, $preloadedacceptances));
 784          $this->assertFalse(api::is_user_version_accepted(13, 6, $preloadedacceptances));
 785          $this->assertNull(api::is_user_version_accepted(13, 5, $preloadedacceptances));
 786      }
 787  
 788      /**
 789       * Test the functionality of {@link api::get_agreement_optional()}.
 790       */
 791      public function test_get_agreement_optional() {
 792          global $DB;
 793          $this->resetAfterTest();
 794          $this->setAdminUser();
 795  
 796          $policy1 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
 797          api::make_current($policy1->id);
 798          $policy2 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
 799          api::make_current($policy2->id);
 800  
 801          $this->assertEquals(api::get_agreement_optional($policy1->id), policy_version::AGREEMENT_OPTIONAL);
 802          $this->assertEquals(api::get_agreement_optional($policy2->id), policy_version::AGREEMENT_COMPULSORY);
 803      }
 804  }