Search moodle.org's
Developer Documentation

See Release Notes

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

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

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