Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
  •    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   * External tests.
      19   *
      20   * @package    core_competency
      21   * @copyright  2016 Frédéric Massart - FMCorz.net
      22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      23   */
      24  
      25  namespace core_competency\external;
      26  
      27  use core_competency\api;
      28  use core_competency\course_competency_settings;
      29  use core_competency\external;
      30  use core_competency\invalid_persistent_exception;
      31  use core_competency\plan;
      32  use core_competency\plan_competency;
      33  use core_competency\related_competency;
      34  use core_competency\template;
      35  use core_competency\template_competency;
      36  use core_competency\user_competency;
      37  use core_competency\user_competency_plan;
      38  use externallib_advanced_testcase;
      39  
      40  defined('MOODLE_INTERNAL') || die();
      41  global $CFG;
      42  
      43  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
      44  
      45  /**
      46   * External testcase.
      47   *
      48   * @package    core_competency
      49   * @copyright  2016 Frédéric Massart - FMCorz.net
      50   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      51   */
      52  class external_test extends externallib_advanced_testcase {
      53  
      54      /** @var \stdClass $creator User with enough permissions to create insystem context. */
      55      protected $creator = null;
      56  
      57      /** @var \stdClass $learningplancreator User with enough permissions to create incategory context. */
      58      protected $catcreator = null;
      59  
      60      /** @var \stdClass $category Category */
      61      protected $category = null;
      62  
      63      /** @var \stdClass $user User with enough permissions to view insystem context */
      64      protected $user = null;
      65  
      66      /** @var \stdClass $catuser User with enough permissions to view incategory context */
      67      protected $catuser = null;
      68  
      69      /** @var int Creator role id */
      70      protected $creatorrole = null;
      71  
      72      /** @var int User role id */
      73      protected $userrole = null;
      74  
      75      /** @var \stdClass $scale1 Scale */
      76      protected $scale1 = null;
      77  
      78      /** @var \stdClass $scale2 Scale */
      79      protected $scale2 = null;
      80  
      81      /** @var \stdClass $scale3 Scale */
      82      protected $scale3 = null;
      83  
      84      /** @var \stdClass $scale4 Scale */
      85      protected $scale4 = null;
      86  
      87      /** @var string scaleconfiguration */
      88      protected $scaleconfiguration1 = null;
      89  
      90      /** @var string scaleconfiguration */
      91      protected $scaleconfiguration2 = null;
      92  
      93      /** @var string catscaleconfiguration */
      94      protected $scaleconfiguration3 = null;
      95  
      96      /** @var string catscaleconfiguration */
      97      protected $catscaleconfiguration4 = null;
      98  
      99      /**
     100       * Setup function- we will create a course and add an assign instance to it.
     101       */
     102      protected function setUp(): void {
     103          global $DB, $CFG;
     104  
     105          $this->resetAfterTest(true);
     106  
     107          // Create some users.
     108          $creator = $this->getDataGenerator()->create_user();
     109          $user = $this->getDataGenerator()->create_user();
     110          $catuser = $this->getDataGenerator()->create_user();
     111          $category = $this->getDataGenerator()->create_category();
     112          $othercategory = $this->getDataGenerator()->create_category();
     113          $catcreator = $this->getDataGenerator()->create_user();
     114  
     115          $syscontext = \context_system::instance();
     116          $catcontext = \context_coursecat::instance($category->id);
     117          $othercatcontext = \context_coursecat::instance($othercategory->id);
     118  
     119          // Fetching default authenticated user role.
     120          $authrole = $DB->get_record('role', array('id' => $CFG->defaultuserroleid));
     121  
     122          // Reset all default authenticated users permissions.
     123          unassign_capability('moodle/competency:competencygrade', $authrole->id);
     124          unassign_capability('moodle/competency:competencymanage', $authrole->id);
     125          unassign_capability('moodle/competency:competencyview', $authrole->id);
     126          unassign_capability('moodle/competency:planmanage', $authrole->id);
     127          unassign_capability('moodle/competency:planmanagedraft', $authrole->id);
     128          unassign_capability('moodle/competency:planmanageown', $authrole->id);
     129          unassign_capability('moodle/competency:planview', $authrole->id);
     130          unassign_capability('moodle/competency:planviewdraft', $authrole->id);
     131          unassign_capability('moodle/competency:planviewown', $authrole->id);
     132          unassign_capability('moodle/competency:planviewowndraft', $authrole->id);
     133          unassign_capability('moodle/competency:templatemanage', $authrole->id);
     134          unassign_capability('moodle/competency:templateview', $authrole->id);
     135          unassign_capability('moodle/cohort:manage', $authrole->id);
     136          unassign_capability('moodle/competency:coursecompetencyconfigure', $authrole->id);
     137  
     138          // Creating specific roles.
     139          $this->creatorrole = create_role('Creator role', 'creatorrole', 'learning plan creator role description');
     140          $this->userrole = create_role('User role', 'userrole', 'learning plan user role description');
     141  
     142          assign_capability('moodle/competency:competencymanage', CAP_ALLOW, $this->creatorrole, $syscontext->id);
     143          assign_capability('moodle/competency:coursecompetencyconfigure', CAP_ALLOW, $this->creatorrole, $syscontext->id);
     144          assign_capability('moodle/competency:competencyview', CAP_ALLOW, $this->userrole, $syscontext->id);
     145          assign_capability('moodle/competency:planmanage', CAP_ALLOW, $this->creatorrole, $syscontext->id);
     146          assign_capability('moodle/competency:planmanagedraft', CAP_ALLOW, $this->creatorrole, $syscontext->id);
     147          assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $this->creatorrole, $syscontext->id);
     148          assign_capability('moodle/competency:planview', CAP_ALLOW, $this->creatorrole, $syscontext->id);
     149          assign_capability('moodle/competency:planviewdraft', CAP_ALLOW, $this->creatorrole, $syscontext->id);
     150          assign_capability('moodle/competency:templatemanage', CAP_ALLOW, $this->creatorrole, $syscontext->id);
     151          assign_capability('moodle/competency:competencygrade', CAP_ALLOW, $this->creatorrole, $syscontext->id);
     152          assign_capability('moodle/cohort:manage', CAP_ALLOW, $this->creatorrole, $syscontext->id);
     153          assign_capability('moodle/competency:templateview', CAP_ALLOW, $this->userrole, $syscontext->id);
     154          assign_capability('moodle/competency:planviewown', CAP_ALLOW, $this->userrole, $syscontext->id);
     155          assign_capability('moodle/competency:planviewowndraft', CAP_ALLOW, $this->userrole, $syscontext->id);
     156  
     157          role_assign($this->creatorrole, $creator->id, $syscontext->id);
     158          role_assign($this->creatorrole, $catcreator->id, $catcontext->id);
     159          role_assign($this->userrole, $user->id, $syscontext->id);
     160          role_assign($this->userrole, $catuser->id, $catcontext->id);
     161  
     162          $this->creator = $creator;
     163          $this->catcreator = $catcreator;
     164          $this->user = $user;
     165          $this->catuser = $catuser;
     166          $this->category = $category;
     167          $this->othercategory = $othercategory;
     168  
     169          $this->scale1 = $this->getDataGenerator()->create_scale(array("scale" => "value1, value2"));
     170          $this->scale2 = $this->getDataGenerator()->create_scale(array("scale" => "value3, value4"));
     171          $this->scale3 = $this->getDataGenerator()->create_scale(array("scale" => "value5, value6"));
     172          $this->scale4 = $this->getDataGenerator()->create_scale(array("scale" => "value7, value8"));
     173  
     174          $this->scaleconfiguration1 = '[{"scaleid":"'.$this->scale1->id.'"},' .
     175                  '{"name":"value1","id":1,"scaledefault":1,"proficient":0},' .
     176                  '{"name":"value2","id":2,"scaledefault":0,"proficient":1}]';
     177          $this->scaleconfiguration2 = '[{"scaleid":"'.$this->scale2->id.'"},' .
     178                  '{"name":"value3","id":1,"scaledefault":1,"proficient":0},' .
     179                  '{"name":"value4","id":2,"scaledefault":0,"proficient":1}]';
     180          $this->scaleconfiguration3 = '[{"scaleid":"'.$this->scale3->id.'"},' .
     181                  '{"name":"value5","id":1,"scaledefault":1,"proficient":0},' .
     182                  '{"name":"value6","id":2,"scaledefault":0,"proficient":1}]';
     183          $this->scaleconfiguration4 = '[{"scaleid":"'.$this->scale4->id.'"},'.
     184                  '{"name":"value8","id":1,"scaledefault":1,"proficient":0},' .
     185                  '{"name":"value8","id":2,"scaledefault":0,"proficient":1}]';
     186          accesslib_clear_all_caches_for_unit_testing();
     187      }
     188  
     189  
     190      protected function create_competency_framework($number = 1, $system = true) {
     191          $scalename = 'scale' . $number;
     192          $scalepropname = 'scaleconfiguration' . $number;
     193          $framework = array(
     194              'shortname' => 'shortname' . $number,
     195              'idnumber' => 'idnumber' . $number,
     196              'description' => 'description' . $number,
     197              'descriptionformat' => FORMAT_HTML,
     198              'scaleid' => $this->$scalename->id,
     199              'scaleconfiguration' => $this->$scalepropname,
     200              'visible' => true,
     201              'contextid' => $system ? \context_system::instance()->id : \context_coursecat::instance($this->category->id)->id
     202          );
     203          $result = external::create_competency_framework($framework);
     204          return (object) \external_api::clean_returnvalue(external::create_competency_framework_returns(), $result);
     205      }
     206  
     207      protected function create_plan($number, $userid, $templateid, $status, $duedate) {
     208          $plan = array(
     209              'name' => 'name' . $number,
     210              'description' => 'description' . $number,
     211              'descriptionformat' => FORMAT_HTML,
     212              'userid' => $userid,
     213              'templateid' => empty($templateid) ? null : $templateid,
     214              'status' => $status,
     215              'duedate' => $duedate
     216          );
     217          $result = external::create_plan($plan);
     218          return (object) \external_api::clean_returnvalue(external::create_plan_returns(), $result);
     219      }
     220  
     221      protected function create_template($number, $system) {
     222          $template = array(
     223              'shortname' => 'shortname' . $number,
     224              'description' => 'description' . $number,
     225              'descriptionformat' => FORMAT_HTML,
     226              'duedate' => 0,
     227              'visible' => true,
     228              'contextid' => $system ? \context_system::instance()->id : \context_coursecat::instance($this->category->id)->id
     229          );
     230          $result = external::create_template($template);
     231          return (object) \external_api::clean_returnvalue(external::create_template_returns(), $result);
     232      }
     233  
     234      protected function update_template($templateid, $number) {
     235          $template = array(
     236              'id' => $templateid,
     237              'shortname' => 'shortname' . $number,
     238              'description' => 'description' . $number,
     239              'descriptionformat' => FORMAT_HTML,
     240              'visible' => true
     241          );
     242          $result = external::update_template($template);
     243          return \external_api::clean_returnvalue(external::update_template_returns(), $result);
     244      }
     245  
     246      protected function update_plan($planid, $number, $userid, $templateid, $status, $duedate) {
     247          $plan = array(
     248              'id' => $planid,
     249              'name' => 'name' . $number,
     250              'description' => 'description' . $number,
     251              'descriptionformat' => FORMAT_HTML,
     252              'userid' => $userid,
     253              'templateid' => $templateid,
     254              'status' => $status,
     255              'duedate' => $duedate
     256          );
     257          $result = external::update_plan($plan);
     258          return \external_api::clean_returnvalue(external::update_plan_returns(), $result);
     259      }
     260  
     261      protected function update_competency_framework($id, $number = 1, $system = true) {
     262          $scalename = 'scale' . $number;
     263          $scalepropname = 'scaleconfiguration' . $number;
     264          $framework = array(
     265              'id' => $id,
     266              'shortname' => 'shortname' . $number,
     267              'idnumber' => 'idnumber' . $number,
     268              'description' => 'description' . $number,
     269              'descriptionformat' => FORMAT_HTML,
     270              'scaleid' => $this->$scalename->id,
     271              'scaleconfiguration' => $this->$scalepropname,
     272              'visible' => true,
     273              'contextid' => $system ? \context_system::instance()->id : \context_coursecat::instance($this->category->id)->id
     274          );
     275          $result = external::update_competency_framework($framework);
     276          return \external_api::clean_returnvalue(external::update_competency_framework_returns(), $result);
     277      }
     278  
     279      protected function create_competency($number, $frameworkid) {
     280          $competency = array(
     281              'shortname' => 'shortname' . $number,
     282              'idnumber' => 'idnumber' . $number,
     283              'description' => 'description' . $number,
     284              'descriptionformat' => FORMAT_HTML,
     285              'competencyframeworkid' => $frameworkid
     286          );
     287          $result = external::create_competency($competency);
     288          return (object) \external_api::clean_returnvalue(external::create_competency_returns(), $result);
     289      }
     290  
     291      protected function update_competency($id, $number) {
     292          $competency = array(
     293              'id' => $id,
     294              'shortname' => 'shortname' . $number,
     295              'idnumber' => 'idnumber' . $number,
     296              'description' => 'description' . $number,
     297              'descriptionformat' => FORMAT_HTML
     298          );
     299          $result = external::update_competency($competency);
     300          return \external_api::clean_returnvalue(external::update_competency_returns(), $result);
     301      }
     302  
     303      /**
     304       * Test we can't create a competency framework with only read permissions.
     305       */
     306      public function test_create_competency_frameworks_with_read_permissions() {
     307          $this->setUser($this->user);
     308  
     309          $this->expectException(\required_capability_exception::class);
     310          $result = $this->create_competency_framework(1, true);
     311      }
     312  
     313      /**
     314       * Test we can't create a competency framework with only read permissions.
     315       */
     316      public function test_create_competency_frameworks_with_read_permissions_in_category() {
     317          $this->setUser($this->catuser);
     318          $this->expectException(\required_capability_exception::class);
     319          $result = $this->create_competency_framework(1, false);
     320      }
     321  
     322      /**
     323       * Test we can create a competency framework with manage permissions.
     324       */
     325      public function test_create_competency_frameworks_with_manage_permissions() {
     326          $this->setUser($this->creator);
     327          $result = $this->create_competency_framework(1, true);
     328  
     329          $this->assertGreaterThan(0, $result->timecreated);
     330          $this->assertGreaterThan(0, $result->timemodified);
     331          $this->assertEquals($this->creator->id, $result->usermodified);
     332          $this->assertEquals('shortname1', $result->shortname);
     333          $this->assertEquals('idnumber1', $result->idnumber);
     334          $this->assertEquals('description1', $result->description);
     335          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     336          $this->assertEquals($this->scale1->id, $result->scaleid);
     337          $this->assertEquals($this->scaleconfiguration1, $result->scaleconfiguration);
     338          $this->assertEquals(true, $result->visible);
     339      }
     340  
     341      /**
     342       * Test we can create a competency framework with manage permissions.
     343       */
     344      public function test_create_competency_frameworks_with_manage_permissions_in_category() {
     345          $this->setUser($this->catcreator);
     346          $result = $this->create_competency_framework(1, false);
     347  
     348          $this->assertGreaterThan(0, $result->timecreated);
     349          $this->assertGreaterThan(0, $result->timemodified);
     350          $this->assertEquals($this->catcreator->id, $result->usermodified);
     351          $this->assertEquals('shortname1', $result->shortname);
     352          $this->assertEquals('idnumber1', $result->idnumber);
     353          $this->assertEquals('description1', $result->description);
     354          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     355          $this->assertEquals($this->scale1->id, $result->scaleid);
     356          $this->assertEquals($this->scaleconfiguration1, $result->scaleconfiguration);
     357          $this->assertEquals(true, $result->visible);
     358  
     359          try {
     360              $result = $this->create_competency_framework(1, true);
     361              $this->fail('User cannot create a framework at system level.');
     362          } catch (\required_capability_exception $e) {
     363              // All good.
     364          }
     365      }
     366  
     367      /**
     368       * Test we cannot create a competency framework with nasty data.
     369       */
     370      public function test_create_competency_frameworks_with_nasty_data() {
     371          $this->setUser($this->creator);
     372          $framework = array(
     373              'shortname' => 'short<a href="">',
     374              'idnumber' => 'id;"number',
     375              'description' => 'de<>\\..scription',
     376              'descriptionformat' => FORMAT_HTML,
     377              'scaleid' => $this->scale1->id,
     378              'scaleconfiguration' => $this->scaleconfiguration1,
     379              'visible' => true,
     380              'contextid' => \context_system::instance()->id
     381          );
     382          $this->expectException(\invalid_parameter_exception::class);
     383          $result = external::create_competency_framework($framework);
     384      }
     385  
     386      /**
     387       * Test we can read a competency framework with manage permissions.
     388       */
     389      public function test_read_competency_frameworks_with_manage_permissions() {
     390          $this->setUser($this->creator);
     391          $result = $this->create_competency_framework(1, true);
     392  
     393          $id = $result->id;
     394          $result = external::read_competency_framework($id);
     395          $result = (object) \external_api::clean_returnvalue(external::read_competency_framework_returns(), $result);
     396  
     397          $this->assertGreaterThan(0, $result->timecreated);
     398          $this->assertGreaterThan(0, $result->timemodified);
     399          $this->assertEquals($this->creator->id, $result->usermodified);
     400          $this->assertEquals('shortname1', $result->shortname);
     401          $this->assertEquals('idnumber1', $result->idnumber);
     402          $this->assertEquals('description1', $result->description);
     403          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     404          $this->assertEquals($this->scale1->id, $result->scaleid);
     405          $this->assertEquals($this->scaleconfiguration1, $result->scaleconfiguration);
     406          $this->assertEquals(true, $result->visible);
     407      }
     408  
     409      /**
     410       * Test we can read a competency framework with manage permissions.
     411       */
     412      public function test_read_competency_frameworks_with_manage_permissions_in_category() {
     413          $this->setUser($this->creator);
     414  
     415          $insystem = $this->create_competency_framework(1, true);
     416          $incat = $this->create_competency_framework(2, false);
     417  
     418          $this->setUser($this->catcreator);
     419          $id = $incat->id;
     420          $result = external::read_competency_framework($id);
     421          $result = (object) \external_api::clean_returnvalue(external::read_competency_framework_returns(), $result);
     422  
     423          $this->assertGreaterThan(0, $result->timecreated);
     424          $this->assertGreaterThan(0, $result->timemodified);
     425          $this->assertEquals($this->creator->id, $result->usermodified);
     426          $this->assertEquals('shortname2', $result->shortname);
     427          $this->assertEquals('idnumber2', $result->idnumber);
     428          $this->assertEquals('description2', $result->description);
     429          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     430          $this->assertEquals($this->scale2->id, $result->scaleid);
     431          $this->assertEquals($this->scaleconfiguration2, $result->scaleconfiguration);
     432          $this->assertEquals(true, $result->visible);
     433  
     434          try {
     435              $id = $insystem->id;
     436              $result = external::read_competency_framework($id);
     437              $result = (object) \external_api::clean_returnvalue(external::read_competency_framework_returns(), $result);
     438              $this->fail('User cannot read a framework at system level.');
     439          } catch (\required_capability_exception $e) {
     440              // All good.
     441          }
     442      }
     443  
     444      /**
     445       * Test we can read a competency framework with read permissions.
     446       */
     447      public function test_read_competency_frameworks_with_read_permissions() {
     448          $this->setUser($this->creator);
     449          $result = $this->create_competency_framework(1, true);
     450  
     451          // Switch users to someone with less permissions.
     452          $this->setUser($this->user);
     453          $id = $result->id;
     454          $result = external::read_competency_framework($id);
     455          $result = (object) \external_api::clean_returnvalue(external::read_competency_framework_returns(), $result);
     456  
     457          $this->assertGreaterThan(0, $result->timecreated);
     458          $this->assertGreaterThan(0, $result->timemodified);
     459          $this->assertEquals($this->creator->id, $result->usermodified);
     460          $this->assertEquals('shortname1', $result->shortname);
     461          $this->assertEquals('idnumber1', $result->idnumber);
     462          $this->assertEquals('description1', $result->description);
     463          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     464          $this->assertEquals($this->scale1->id, $result->scaleid);
     465          $this->assertEquals($this->scaleconfiguration1, $result->scaleconfiguration);
     466          $this->assertEquals(true, $result->visible);
     467      }
     468      /**
     469       * Test we can read a competency framework with read permissions.
     470       */
     471      public function test_read_competency_frameworks_with_read_permissions_in_category() {
     472          $this->setUser($this->creator);
     473  
     474          $insystem = $this->create_competency_framework(1, true);
     475          $incat = $this->create_competency_framework(2, false);
     476  
     477          // Switch users to someone with less permissions.
     478          $this->setUser($this->catuser);
     479          $id = $incat->id;
     480          $result = external::read_competency_framework($id);
     481          $result = (object) \external_api::clean_returnvalue(external::read_competency_framework_returns(), $result);
     482  
     483          $this->assertGreaterThan(0, $result->timecreated);
     484          $this->assertGreaterThan(0, $result->timemodified);
     485          $this->assertEquals($this->creator->id, $result->usermodified);
     486          $this->assertEquals('shortname2', $result->shortname);
     487          $this->assertEquals('idnumber2', $result->idnumber);
     488          $this->assertEquals('description2', $result->description);
     489          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     490          $this->assertEquals($this->scale2->id, $result->scaleid);
     491          $this->assertEquals($this->scaleconfiguration2, $result->scaleconfiguration);
     492          $this->assertEquals(true, $result->visible);
     493  
     494          // Switching to user with no permissions.
     495          try {
     496              $result = external::read_competency_framework($insystem->id);
     497              $this->fail('Current user cannot should not be able to read the framework.');
     498          } catch (\required_capability_exception $e) {
     499              // All good.
     500          }
     501      }
     502  
     503      /**
     504       * Test we can delete a competency framework with manage permissions.
     505       */
     506      public function test_delete_competency_frameworks_with_manage_permissions() {
     507          $this->setUser($this->creator);
     508          $result = $this->create_competency_framework(1, true);
     509  
     510          $id = $result->id;
     511          $result = external::delete_competency_framework($id);
     512          $result = \external_api::clean_returnvalue(external::delete_competency_framework_returns(), $result);
     513  
     514          $this->assertTrue($result);
     515      }
     516  
     517      /**
     518       * Test we can delete a competency framework with manage permissions.
     519       */
     520      public function test_delete_competency_frameworks_with_manage_permissions_in_category() {
     521          $this->setUser($this->creator);
     522  
     523          $insystem = $this->create_competency_framework(1, true);
     524          $incat = $this->create_competency_framework(2, false);
     525  
     526          $this->setUser($this->catcreator);
     527          $id = $incat->id;
     528          $result = external::delete_competency_framework($id);
     529          $result = \external_api::clean_returnvalue(external::delete_competency_framework_returns(), $result);
     530  
     531          $this->assertTrue($result);
     532  
     533          try {
     534              $id = $insystem->id;
     535              $result = external::delete_competency_framework($id);
     536              $result = \external_api::clean_returnvalue(external::delete_competency_framework_returns(), $result);
     537              $this->fail('Current user cannot should not be able to delete the framework.');
     538          } catch (\required_capability_exception $e) {
     539              // All good.
     540          }
     541      }
     542  
     543      /**
     544       * Test we can delete a competency framework with read permissions.
     545       */
     546      public function test_delete_competency_frameworks_with_read_permissions() {
     547          $this->setUser($this->creator);
     548          $result = $this->create_competency_framework(1, true);
     549  
     550          $id = $result->id;
     551          // Switch users to someone with less permissions.
     552          $this->setUser($this->user);
     553          $this->expectException(\required_capability_exception::class);
     554          $result = external::delete_competency_framework($id);
     555      }
     556  
     557      /**
     558       * Test we can update a competency framework with manage permissions.
     559       */
     560      public function test_update_competency_frameworks_with_manage_permissions() {
     561          $this->setUser($this->creator);
     562          $result = $this->create_competency_framework(1, true);
     563  
     564          $result = $this->update_competency_framework($result->id, 2, true);
     565  
     566          $this->assertTrue($result);
     567      }
     568  
     569      /**
     570       * Test we can update a competency framework with manage permissions.
     571       */
     572      public function test_update_competency_frameworks_with_manage_permissions_in_category() {
     573          $this->setUser($this->creator);
     574  
     575          $insystem = $this->create_competency_framework(1, true);
     576          $incat = $this->create_competency_framework(2, false);
     577  
     578          $this->setUser($this->catcreator);
     579          $id = $incat->id;
     580  
     581          $result = $this->update_competency_framework($incat->id, 3, false);
     582  
     583          $this->assertTrue($result);
     584  
     585          try {
     586              $result = $this->update_competency_framework($insystem->id, 4, true);
     587              $this->fail('Current user should not be able to update the framework.');
     588          } catch (\required_capability_exception $e) {
     589              // All good.
     590          }
     591      }
     592  
     593      public function test_update_framework_scale() {
     594          $this->setUser($this->creator);
     595          $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
     596  
     597          $s1 = $this->getDataGenerator()->create_scale();
     598  
     599          $f1 = $lpg->create_framework(array('scaleid' => $s1->id));
     600          $f2 = $lpg->create_framework(array('scaleid' => $s1->id));
     601          $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
     602          $c2 = $lpg->create_competency(array('competencyframeworkid' => $f2->get('id')));
     603  
     604          $this->assertEquals($s1->id, $f1->get('scaleid'));
     605  
     606          // Make the scale of f2 being used.
     607          $lpg->create_user_competency(array('userid' => $this->user->id, 'competencyid' => $c2->get('id')));
     608  
     609          // Changing the framework where the scale is not used.
     610          $result = $this->update_competency_framework($f1->get('id'), 3, true);
     611  
     612          $f1 = new \core_competency\competency_framework($f1->get('id'));
     613          $this->assertEquals($this->scale3->id, $f1->get('scaleid'));
     614  
     615          // Changing the framework where the scale is used.
     616          try {
     617              $result = $this->update_competency_framework($f2->get('id'), 4, true);
     618              $this->fail('The scale cannot be changed once used.');
     619          } catch (\core\invalid_persistent_exception $e) {
     620              $this->assertMatchesRegularExpression('/scaleid/', $e->getMessage());
     621          }
     622      }
     623  
     624      /**
     625       * Test we can update a competency framework with read permissions.
     626       */
     627      public function test_update_competency_frameworks_with_read_permissions() {
     628          $this->setUser($this->creator);
     629          $result = $this->create_competency_framework(1, true);
     630  
     631          $this->setUser($this->user);
     632          $this->expectException(\required_capability_exception::class);
     633          $result = $this->update_competency_framework($result->id, 2, true);
     634      }
     635  
     636      /**
     637       * Test we can list and count competency frameworks with manage permissions.
     638       */
     639      public function test_list_and_count_competency_frameworks_with_manage_permissions() {
     640          $this->setUser($this->creator);
     641          $result = $this->create_competency_framework(1, true);
     642          $result = $this->create_competency_framework(2, true);
     643          $result = $this->create_competency_framework(3, true);
     644          $result = $this->create_competency_framework(4, false);
     645  
     646          $result = external::count_competency_frameworks(array('contextid' => \context_system::instance()->id), 'self');
     647          $result = \external_api::clean_returnvalue(external::count_competency_frameworks_returns(), $result);
     648  
     649          $this->assertEquals($result, 3);
     650  
     651          $result = external::list_competency_frameworks('shortname', 'ASC', 0, 10,
     652              array('contextid' => \context_system::instance()->id), 'self', false);
     653          $result = \external_api::clean_returnvalue(external::list_competency_frameworks_returns(), $result);
     654  
     655          $this->assertEquals(count($result), 3);
     656          $result = (object) $result[0];
     657  
     658          $this->assertGreaterThan(0, $result->timecreated);
     659          $this->assertGreaterThan(0, $result->timemodified);
     660          $this->assertEquals($this->creator->id, $result->usermodified);
     661          $this->assertEquals('shortname1', $result->shortname);
     662          $this->assertEquals('idnumber1', $result->idnumber);
     663          $this->assertEquals('description1', $result->description);
     664          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     665          $this->assertEquals($this->scale1->id, $result->scaleid);
     666          $this->assertEquals($this->scaleconfiguration1, $result->scaleconfiguration);
     667          $this->assertEquals(true, $result->visible);
     668      }
     669  
     670      public function test_list_competency_frameworks_with_query() {
     671          $this->setUser($this->creator);
     672          $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
     673          $framework1 = $lpg->create_framework(array(
     674              'shortname' => 'shortname_beetroot',
     675              'idnumber' => 'idnumber_cinnamon',
     676              'description' => 'description',
     677              'descriptionformat' => FORMAT_HTML,
     678              'visible' => true,
     679              'contextid' => \context_system::instance()->id
     680          ));
     681          $framework2 = $lpg->create_framework(array(
     682              'shortname' => 'shortname_citrus',
     683              'idnumber' => 'idnumber_beer',
     684              'description' => 'description',
     685              'descriptionformat' => FORMAT_HTML,
     686              'visible' => true,
     687              'contextid' => \context_system::instance()->id
     688          ));
     689  
     690          // Search on both ID number and shortname.
     691          $result = external::list_competency_frameworks('shortname', 'ASC', 0, 10,
     692              array('contextid' => \context_system::instance()->id), 'self', false, 'bee');
     693          $result = \external_api::clean_returnvalue(external::list_competency_frameworks_returns(), $result);
     694          $this->assertCount(2, $result);
     695          $f = (object) array_shift($result);
     696          $this->assertEquals($framework1->get('id'), $f->id);
     697          $f = (object) array_shift($result);
     698          $this->assertEquals($framework2->get('id'), $f->id);
     699  
     700          // Search on ID number.
     701          $result = external::list_competency_frameworks('shortname', 'ASC', 0, 10,
     702              array('contextid' => \context_system::instance()->id), 'self', false, 'beer');
     703          $result = \external_api::clean_returnvalue(external::list_competency_frameworks_returns(), $result);
     704          $this->assertCount(1, $result);
     705          $f = (object) array_shift($result);
     706          $this->assertEquals($framework2->get('id'), $f->id);
     707  
     708          // Search on shortname.
     709          $result = external::list_competency_frameworks('shortname', 'ASC', 0, 10,
     710              array('contextid' => \context_system::instance()->id), 'self', false, 'cinnamon');
     711          $result = \external_api::clean_returnvalue(external::list_competency_frameworks_returns(), $result);
     712          $this->assertCount(1, $result);
     713          $f = (object) array_shift($result);
     714          $this->assertEquals($framework1->get('id'), $f->id);
     715  
     716          // No match.
     717          $result = external::list_competency_frameworks('shortname', 'ASC', 0, 10,
     718              array('contextid' => \context_system::instance()->id), 'self', false, 'pwnd!');
     719          $result = \external_api::clean_returnvalue(external::list_competency_frameworks_returns(), $result);
     720          $this->assertCount(0, $result);
     721      }
     722  
     723      /**
     724       * Test we can list and count competency frameworks with read permissions.
     725       */
     726      public function test_list_and_count_competency_frameworks_with_read_permissions() {
     727          $this->setUser($this->creator);
     728          $result = $this->create_competency_framework(1, true);
     729          $result = $this->create_competency_framework(2, true);
     730          $result = $this->create_competency_framework(3, true);
     731          $result = $this->create_competency_framework(4, false);
     732  
     733          $this->setUser($this->user);
     734          $result = external::count_competency_frameworks(array('contextid' => \context_system::instance()->id), 'self');
     735          $result = \external_api::clean_returnvalue(external::count_competency_frameworks_returns(), $result);
     736          $this->assertEquals($result, 3);
     737  
     738          $result = external::list_competency_frameworks('shortname', 'ASC', 0, 10,
     739              array('contextid' => \context_system::instance()->id), 'self', false);
     740          $result = \external_api::clean_returnvalue(external::list_competency_frameworks_returns(), $result);
     741  
     742          $this->assertEquals(count($result), 3);
     743          $result = (object) $result[0];
     744  
     745          $this->assertGreaterThan(0, $result->timecreated);
     746          $this->assertGreaterThan(0, $result->timemodified);
     747          $this->assertEquals($this->creator->id, $result->usermodified);
     748          $this->assertEquals('shortname1', $result->shortname);
     749          $this->assertEquals('idnumber1', $result->idnumber);
     750          $this->assertEquals('description1', $result->description);
     751          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     752          $this->assertEquals($this->scale1->id, $result->scaleid);
     753          $this->assertEquals($this->scaleconfiguration1, $result->scaleconfiguration);
     754          $this->assertEquals(true, $result->visible);
     755      }
     756  
     757      /**
     758       * Test we can't create a competency with only read permissions.
     759       */
     760      public function test_create_competency_with_read_permissions() {
     761          $framework = $this->getDataGenerator()->get_plugin_generator('core_competency')->create_framework();
     762          $this->setUser($this->user);
     763          $this->expectException(\required_capability_exception::class);
     764          $competency = $this->create_competency(1, $framework->get('id'));
     765      }
     766  
     767      /**
     768       * Test we can create a competency with manage permissions.
     769       */
     770      public function test_create_competency_with_manage_permissions() {
     771          $this->setUser($this->creator);
     772          $framework = $this->create_competency_framework(1, true);
     773          $competency = $this->create_competency(1, $framework->id);
     774  
     775          $this->assertGreaterThan(0, $competency->timecreated);
     776          $this->assertGreaterThan(0, $competency->timemodified);
     777          $this->assertEquals($this->creator->id, $competency->usermodified);
     778          $this->assertEquals('shortname1', $competency->shortname);
     779          $this->assertEquals('idnumber1', $competency->idnumber);
     780          $this->assertEquals('description1', $competency->description);
     781          $this->assertEquals(FORMAT_HTML, $competency->descriptionformat);
     782          $this->assertEquals(0, $competency->parentid);
     783          $this->assertEquals($framework->id, $competency->competencyframeworkid);
     784      }
     785  
     786  
     787      /**
     788       * Test we can create a competency with manage permissions.
     789       */
     790      public function test_create_competency_with_manage_permissions_in_category() {
     791          $this->setUser($this->creator);
     792  
     793          $insystem = $this->create_competency_framework(1, true);
     794          $incat = $this->create_competency_framework(2, false);
     795  
     796          $this->setUser($this->catcreator);
     797  
     798          $competency = $this->create_competency(1, $incat->id);
     799  
     800          $this->assertGreaterThan(0, $competency->timecreated);
     801          $this->assertGreaterThan(0, $competency->timemodified);
     802          $this->assertEquals($this->catcreator->id, $competency->usermodified);
     803          $this->assertEquals('shortname1', $competency->shortname);
     804          $this->assertEquals('idnumber1', $competency->idnumber);
     805          $this->assertEquals('description1', $competency->description);
     806          $this->assertEquals(FORMAT_HTML, $competency->descriptionformat);
     807          $this->assertEquals(0, $competency->parentid);
     808          $this->assertEquals($incat->id, $competency->competencyframeworkid);
     809  
     810          try {
     811              $competency = $this->create_competency(2, $insystem->id);
     812              $this->fail('User should not be able to create a competency in system context.');
     813          } catch (\required_capability_exception $e) {
     814              // All good.
     815          }
     816      }
     817  
     818      /**
     819       * Test we cannot create a competency with nasty data.
     820       */
     821      public function test_create_competency_with_nasty_data() {
     822          $this->setUser($this->creator);
     823          $framework = $this->create_competency_framework(1, true);
     824          $competency = array(
     825              'shortname' => 'shortname<a href="">',
     826              'idnumber' => 'id;"number',
     827              'description' => 'de<>\\..scription',
     828              'descriptionformat' => FORMAT_HTML,
     829              'competencyframeworkid' => $framework->id,
     830              'sortorder' => 0
     831          );
     832  
     833          $this->expectException(\invalid_parameter_exception::class);
     834          $this->expectExceptionMessage('Invalid external api parameter');
     835          $result = external::create_competency($competency);
     836      }
     837  
     838      /**
     839       * Test we can read a competency with manage permissions.
     840       */
     841      public function test_read_competencies_with_manage_permissions() {
     842          $this->setUser($this->creator);
     843          $framework = $this->create_competency_framework(1, true);
     844          $competency = $this->create_competency(1, $framework->id);
     845  
     846          $id = $competency->id;
     847          $result = external::read_competency($id);
     848          $result = (object) \external_api::clean_returnvalue(external::read_competency_returns(), $result);
     849  
     850          $this->assertGreaterThan(0, $result->timecreated);
     851          $this->assertGreaterThan(0, $result->timemodified);
     852          $this->assertEquals($this->creator->id, $result->usermodified);
     853          $this->assertEquals('shortname1', $result->shortname);
     854          $this->assertEquals('idnumber1', $result->idnumber);
     855          $this->assertEquals('description1', $result->description);
     856          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     857          $this->assertEquals(0, $result->parentid);
     858          $this->assertEquals($framework->id, $result->competencyframeworkid);
     859      }
     860  
     861      /**
     862       * Test we can read a competency with manage permissions.
     863       */
     864      public function test_read_competencies_with_manage_permissions_in_category() {
     865          $this->setUser($this->creator);
     866  
     867          $sysframework = $this->create_competency_framework(1, true);
     868          $insystem = $this->create_competency(1, $sysframework->id);
     869  
     870          $catframework = $this->create_competency_framework(2, false);
     871          $incat = $this->create_competency(2, $catframework->id);
     872  
     873          $this->setUser($this->catcreator);
     874          $id = $incat->id;
     875          $result = external::read_competency($id);
     876          $result = (object) \external_api::clean_returnvalue(external::read_competency_returns(), $result);
     877  
     878          $this->assertGreaterThan(0, $result->timecreated);
     879          $this->assertGreaterThan(0, $result->timemodified);
     880          $this->assertEquals($this->creator->id, $result->usermodified);
     881          $this->assertEquals('shortname2', $result->shortname);
     882          $this->assertEquals('idnumber2', $result->idnumber);
     883          $this->assertEquals('description2', $result->description);
     884          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     885          $this->assertEquals(0, $result->parentid);
     886          $this->assertEquals($catframework->id, $result->competencyframeworkid);
     887  
     888          try {
     889              external::read_competency($insystem->id);
     890              $this->fail('User should not be able to read a competency in system context.');
     891          } catch (\required_capability_exception $e) {
     892              // All good.
     893          }
     894      }
     895  
     896      /**
     897       * Test we can read a competency with read permissions.
     898       */
     899      public function test_read_competencies_with_read_permissions() {
     900          $this->setUser($this->creator);
     901          $framework = $this->create_competency_framework(1, true);
     902          $competency = $this->create_competency(1, $framework->id);
     903  
     904          // Switch users to someone with less permissions.
     905          $this->setUser($this->user);
     906          $id = $competency->id;
     907          $result = external::read_competency($id);
     908          $result = (object) \external_api::clean_returnvalue(external::read_competency_returns(), $result);
     909  
     910          $this->assertGreaterThan(0, $result->timecreated);
     911          $this->assertGreaterThan(0, $result->timemodified);
     912          $this->assertEquals($this->creator->id, $result->usermodified);
     913          $this->assertEquals('shortname1', $result->shortname);
     914          $this->assertEquals('idnumber1', $result->idnumber);
     915          $this->assertEquals('description1', $result->description);
     916          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     917          $this->assertEquals(0, $result->parentid);
     918          $this->assertEquals($framework->id, $result->competencyframeworkid);
     919      }
     920  
     921      /**
     922       * Test we can read a competency with read permissions.
     923       */
     924      public function test_read_competencies_with_read_permissions_in_category() {
     925          $this->setUser($this->creator);
     926          $sysframework = $this->create_competency_framework(1, true);
     927          $insystem = $this->create_competency(1, $sysframework->id);
     928          $catframework = $this->create_competency_framework(2, false);
     929          $incat = $this->create_competency(2, $catframework->id);
     930  
     931          // Switch users to someone with less permissions.
     932          $this->setUser($this->catuser);
     933          $id = $incat->id;
     934          $result = external::read_competency($id);
     935          $result = (object) \external_api::clean_returnvalue(external::read_competency_returns(), $result);
     936  
     937          $this->assertGreaterThan(0, $result->timecreated);
     938          $this->assertGreaterThan(0, $result->timemodified);
     939          $this->assertEquals($this->creator->id, $result->usermodified);
     940          $this->assertEquals('shortname2', $result->shortname);
     941          $this->assertEquals('idnumber2', $result->idnumber);
     942          $this->assertEquals('description2', $result->description);
     943          $this->assertEquals(FORMAT_HTML, $result->descriptionformat);
     944          $this->assertEquals(0, $result->parentid);
     945          $this->assertEquals($catframework->id, $result->competencyframeworkid);
     946  
     947          try {
     948              external::read_competency($insystem->id);
     949              $this->fail('User should not be able to read a competency in system context.');
     950          } catch (\required_capability_exception $e) {
     951              // All good.
     952          }
     953      }
     954  
     955      /**
     956       * Test we can delete a competency with manage permissions.
     957       */
     958      public function test_delete_competency_with_manage_permissions() {
     959          $this->setUser($this->creator);
     960          $framework = $this->create_competency_framework(1, true);
     961          $result = $this->create_competency(1, $framework->id);
     962  
     963          $id = $result->id;
     964          $result = external::delete_competency($id);
     965          $result = \external_api::clean_returnvalue(external::delete_competency_returns(), $result);
     966  
     967          $this->assertTrue($result);
     968      }
     969  
     970      /**
     971       * Test we can delete a competency with manage permissions.
     972       */
     973      public function test_delete_competency_with_manage_permissions_in_category() {
     974          $this->setUser($this->creator);
     975  
     976          $sysframework = $this->create_competency_framework(1, true);
     977          $insystem = $this->create_competency(1, $sysframework->id);
     978          $catframework = $this->create_competency_framework(2, false);
     979          $incat = $this->create_competency(2, $catframework->id);
     980  
     981          $this->setUser($this->catcreator);
     982          $id = $incat->id;
     983          $result = external::delete_competency($id);
     984          $result = \external_api::clean_returnvalue(external::delete_competency_returns(), $result);
     985  
     986          $this->assertTrue($result);
     987  
     988          try {
     989              $result = external::delete_competency($insystem->id);
     990              $this->fail('User should not be able to delete a competency in system context.');
     991          } catch (\required_capability_exception $e) {
     992              // All good.
     993          }
     994      }
     995  
     996      /**
     997       * Test we can delete a competency with read permissions.
     998       */
     999      public function test_delete_competency_with_read_permissions() {
    1000          $this->setUser($this->creator);
    1001          $framework = $this->create_competency_framework(1, true);
    1002          $result = $this->create_competency(1, $framework->id);
    1003  
    1004          $id = $result->id;
    1005          // Switch users to someone with less permissions.
    1006          $this->setUser($this->user);
    1007          $this->expectException(\required_capability_exception::class);
    1008          $result = external::delete_competency($id);
    1009      }
    1010  
    1011      /**
    1012       * Test we can update a competency with manage permissions.
    1013       */
    1014      public function test_update_competency_with_manage_permissions() {
    1015          $this->setUser($this->creator);
    1016          $framework = $this->create_competency_framework(1, true);
    1017          $result = $this->create_competency(1, $framework->id);
    1018  
    1019          $result = $this->update_competency($result->id, 2);
    1020  
    1021          $this->assertTrue($result);
    1022      }
    1023  
    1024      /**
    1025       * Test we can update a competency with manage permissions.
    1026       */
    1027      public function test_update_competency_with_manage_permissions_in_category() {
    1028          $this->setUser($this->creator);
    1029  
    1030          $sysframework = $this->create_competency_framework(1, true);
    1031          $insystem = $this->create_competency(1, $sysframework->id);
    1032          $catframework = $this->create_competency_framework(2, false);
    1033          $incat = $this->create_competency(2, $catframework->id);
    1034  
    1035          $this->setUser($this->catcreator);
    1036  
    1037          $result = $this->update_competency($incat->id, 2);
    1038  
    1039          $this->assertTrue($result);
    1040  
    1041          try {
    1042              $result = $this->update_competency($insystem->id, 3);
    1043              $this->fail('User should not be able to update a competency in system context.');
    1044          } catch (\required_capability_exception $e) {
    1045              // All good.
    1046          }
    1047      }
    1048  
    1049      /**
    1050       * Test we can update a competency with read permissions.
    1051       */
    1052      public function test_update_competency_with_read_permissions() {
    1053          $this->setUser($this->creator);
    1054          $framework = $this->create_competency_framework(1, true);
    1055          $result = $this->create_competency(1, $framework->id);
    1056  
    1057          $this->setUser($this->user);
    1058          $this->expectException(\required_capability_exception::class);
    1059          $result = $this->update_competency($result->id, 2);
    1060      }
    1061  
    1062      /**
    1063       * Test count competencies with filters.
    1064       */
    1065      public function test_count_competencies_with_filters() {
    1066          $this->setUser($this->creator);
    1067  
    1068          $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
    1069          $f1 = $lpg->create_framework();
    1070          $f2 = $lpg->create_framework();
    1071          $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
    1072          $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'shortname' => 'A'));
    1073          $c3 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
    1074          $c4 = $lpg->create_competency(array('competencyframeworkid' => $f2->get('id')));
    1075          $c5 = $lpg->create_competency(array('competencyframeworkid' => $f2->get('id')));
    1076  
    1077          $result = external::count_competencies(array(array('column' => 'competencyframeworkid', 'value' => $f2->get('id'))));
    1078          $result = \external_api::clean_returnvalue(external::count_competencies_returns(), $result);
    1079          $this->assertEquals(2, $result);
    1080  
    1081          $result = external::count_competencies(array(array('column' => 'competencyframeworkid', 'value' => $f1->get('id'))));
    1082          $result = \external_api::clean_returnvalue(external::count_competencies_returns(), $result);
    1083          $this->assertEquals(3, $result);
    1084  
    1085          $result = external::count_competencies(array(array('column' => 'shortname', 'value' => 'A')));
    1086          $result = \external_api::clean_returnvalue(external::count_competencies_returns(), $result);
    1087          $this->assertEquals(1, $result);
    1088      }
    1089  
    1090      /**
    1091       * Test we can list and count competencies with manage permissions.
    1092       */
    1093      public function test_list_and_count_competencies_with_manage_permissions() {
    1094          $this->setUser($this->creator);
    1095          $framework = $this->create_competency_framework(1, true);
    1096          $result = $this->create_competency(1, $framework->id);
    1097          $result = $this->create_competency(2, $framework->id);
    1098          $result = $this->create_competency(3, $framework->id);
    1099  
    1100          $result = external::count_competencies(array());
    1101          $result = \external_api::clean_returnvalue(external::count_competencies_returns(), $result);
    1102  
    1103          $this->assertEquals($result, 3);
    1104  
    1105          array('id' => $result = external::list_competencies(array(), 'shortname', 'ASC', 0, 10, \context_system::instance()->id));
    1106          $result = \external_api::clean_returnvalue(external::list_competencies_returns(), $result);
    1107  
    1108          $this->assertEquals(count($result), 3);
    1109          $result = (object) $result[0];
    1110  
    1111          $this->assertGreaterThan(0, $result->timecreated);
    1112          $this->assertGreaterThan(0, $result->timemodified);
    1113          $this->assertEquals($this->creator->id, $result->usermodified);
    1114          $this->assertEquals('shortname1', $result->shortname);
    1115          $this->assertEquals('idnumber1', $result->idnumber);
    1116          $this->assertEquals('description1', $result->description);
    1117      }
    1118  
    1119      /**
    1120       * Test we can list and count competencies with read permissions.
    1121       */
    1122      public function test_list_and_count_competencies_with_read_permissions() {
    1123          $this->setUser($this->creator);
    1124          $framework = $this->create_competency_framework(1, true);
    1125          $result = $this->create_competency(1, $framework->id);
    1126          $result = $this->create_competency(2, $framework->id);
    1127          $result = $this->create_competency(3, $framework->id);
    1128  
    1129          $this->setUser($this->user);
    1130  
    1131          $result = external::count_competencies(array());
    1132          $result = \external_api::clean_returnvalue(external::count_competencies_returns(), $result);
    1133  
    1134          $this->assertEquals($result, 3);
    1135  
    1136          array('id' => $result = external::list_competencies(array(), 'shortname', 'ASC', 0, 10, \context_system::instance()->id));
    1137          $result = \external_api::clean_returnvalue(external::list_competencies_returns(), $result);
    1138  
    1139          $this->assertEquals(count($result), 3);
    1140          $result = (object) $result[0];
    1141  
    1142          $this->assertGreaterThan(0, $result->timecreated);
    1143          $this->assertGreaterThan(0, $result->timemodified);
    1144          $this->assertEquals($this->creator->id, $result->usermodified);
    1145          $this->assertEquals('shortname1', $result->shortname);
    1146          $this->assertEquals('idnumber1', $result->idnumber);
    1147          $this->assertEquals('description1', $result->description);
    1148      }
    1149  
    1150      /**
    1151       * Test we can search for competencies.
    1152       */
    1153      public function test_search_competencies_with_read_permissions() {
    1154          $this->setUser($this->creator);
    1155          $framework = $this->create_competency_framework(1, true);
    1156          $result = $this->create_competency(1, $framework->id);
    1157          $result = $this->create_competency(2, $framework->id);
    1158          $result = $this->create_competency(3, $framework->id);
    1159  
    1160          $this->setUser($this->user);
    1161  
    1162          $result = external::search_competencies('short', $framework->id);
    1163          $result = \external_api::clean_returnvalue(external::search_competencies_returns(), $result);
    1164  
    1165          $this->assertEquals(count($result), 3);
    1166          $result = (object) $result[0];
    1167  
    1168          $this->assertGreaterThan(0, $result->timecreated);
    1169          $this->assertGreaterThan(0, $result->timemodified);
    1170          $this->assertEquals($this->creator->id, $result->usermodified);
    1171          $this->assertEquals('shortname1', $result->shortname);
    1172          $this->assertEquals('idnumber1', $result->idnumber);
    1173          $this->assertEquals('description1', $result->description);
    1174      }
    1175  
    1176      /**
    1177       * Test plans creation and updates.
    1178       */
    1179      public function test_create_and_update_plans() {
    1180          $syscontext = \context_system::instance();
    1181  
    1182          $this->setUser($this->creator);
    1183          $plan0 = $this->create_plan(1, $this->creator->id, 0, plan::STATUS_ACTIVE, 0);
    1184  
    1185          $this->setUser($this->user);
    1186  
    1187          try {
    1188              $plan1 = $this->create_plan(2, $this->user->id, 0, plan::STATUS_DRAFT, 0);
    1189              $this->fail('Exception expected due to not permissions to create draft plans');
    1190          } catch (\moodle_exception $e) {
    1191              $this->assertEquals('nopermissions', $e->errorcode);
    1192          }
    1193  
    1194          assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $this->userrole, $syscontext->id);
    1195          accesslib_clear_all_caches_for_unit_testing();
    1196  
    1197          $this->setUser($this->user);
    1198  
    1199          $plan2 = $this->create_plan(3, $this->user->id, 0, plan::STATUS_DRAFT, 0);
    1200  
    1201          // Basic update on the plan.
    1202          $this->assertNotEquals('Updated plan 2 name', $plan2->name);
    1203          $plan2 = external::update_plan(['id' => $plan2->id, 'name' => 'Updated plan 2 name']);
    1204          $this->assertEquals('Updated plan 2 name', $plan2->name);
    1205  
    1206          try {
    1207              $plan3 = $this->create_plan(4, $this->user->id, 0, plan::STATUS_ACTIVE, 0);
    1208              $this->fail('Exception expected due to not permissions to create active plans');
    1209          } catch (\moodle_exception $e) {
    1210              $this->assertEquals('nopermissions', $e->errorcode);
    1211          }
    1212          try {
    1213              $plan3 = $this->update_plan($plan2->id, 4, $this->user->id, 0, plan::STATUS_COMPLETE, 0);
    1214              $this->fail('We cannot complete a plan using api::update_plan().');
    1215          } catch (\coding_exception $e) {
    1216              $this->assertTrue(true);
    1217          }
    1218  
    1219          assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $this->userrole, $syscontext->id);
    1220          accesslib_clear_all_caches_for_unit_testing();
    1221  
    1222          $plan3 = $this->create_plan(4, $this->user->id, 0, plan::STATUS_ACTIVE, 0);
    1223          try {
    1224              $plan4 = $this->create_plan(6, $this->creator->id, 0, plan::STATUS_COMPLETE, 0);
    1225              $this->fail('Plans cannot be created as complete.');
    1226          } catch (\coding_exception $e) {
    1227              $this->assertMatchesRegularExpression('/A plan cannot be created as complete./', $e->getMessage());
    1228          }
    1229  
    1230          try {
    1231              $plan0 = $this->update_plan($plan0->id, 1, $this->user->id, 0, plan::STATUS_ACTIVE, 0);
    1232          } catch (\moodle_exception $e) {
    1233              $this->assertEquals('nopermissions', $e->errorcode);
    1234          }
    1235  
    1236          unassign_capability('moodle/competency:planmanageown', $this->userrole, $syscontext->id);
    1237          unassign_capability('moodle/competency:planmanageowndraft', $this->userrole, $syscontext->id);
    1238          accesslib_clear_all_caches_for_unit_testing();
    1239  
    1240          try {
    1241              // Cannot be updated even if they created it.
    1242              $this->update_plan($plan2->id, 1, $this->user->id, 0, plan::STATUS_ACTIVE, 0);
    1243              $this->fail('The user can not update their own plan without permissions.');
    1244          } catch (\required_capability_exception $e) {
    1245              $this->assertMatchesRegularExpression('/Manage learning plans./', $e->getMessage());
    1246          }
    1247      }
    1248  
    1249      /**
    1250       * Test complete plan.
    1251       */
    1252      public function test_complete_plan() {
    1253          $syscontext = \context_system::instance();
    1254  
    1255          $this->setUser($this->creator);
    1256  
    1257          $this->setUser($this->user);
    1258  
    1259          assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $this->userrole, $syscontext->id);
    1260          assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $this->userrole, $syscontext->id);
    1261          accesslib_clear_all_caches_for_unit_testing();
    1262  
    1263          $this->setUser($this->user);
    1264  
    1265          $plan = $this->create_plan(1, $this->user->id, 0, plan::STATUS_ACTIVE, 0);
    1266  
    1267          $result = external::complete_plan($plan->id);
    1268          $this->assertTrue($result);
    1269      }
    1270  
    1271      /**
    1272       * Test reopen plan.
    1273       */
    1274      public function test_reopen_plan() {
    1275          $syscontext = \context_system::instance();
    1276  
    1277          $this->setUser($this->creator);
    1278  
    1279          $this->setUser($this->user);
    1280  
    1281          assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $this->userrole, $syscontext->id);
    1282          assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $this->userrole, $syscontext->id);
    1283          accesslib_clear_all_caches_for_unit_testing();
    1284  
    1285          $this->setUser($this->user);
    1286  
    1287          $plan = $this->create_plan(1, $this->user->id, 0, plan::STATUS_ACTIVE, 0);
    1288          external::complete_plan($plan->id);
    1289  
    1290          $result = external::reopen_plan($plan->id);
    1291          $this->assertTrue($result);
    1292      }
    1293  
    1294      /**
    1295       * Test that we can read plans.
    1296       */
    1297      public function test_read_plans() {
    1298          global $OUTPUT;
    1299          $this->setUser($this->creator);
    1300  
    1301          $syscontext = \context_system::instance();
    1302  
    1303          $plan1 = $this->create_plan(1, $this->user->id, 0, plan::STATUS_DRAFT, 0);
    1304          $plan2 = $this->create_plan(2, $this->user->id, 0, plan::STATUS_ACTIVE, 0);
    1305          $plan3 = $this->create_plan(3, $this->user->id, 0, plan::STATUS_ACTIVE, 0);
    1306          external::complete_plan($plan3->id);
    1307          $plan3 = (object) external::read_plan($plan3->id);
    1308  
    1309          $data = external::read_plan($plan1->id);
    1310          $this->assertEquals((array)$plan1, external::read_plan($plan1->id));
    1311          $data = external::read_plan($plan2->id);
    1312          $this->assertEquals((array)$plan2, external::read_plan($plan2->id));
    1313          $data = external::read_plan($plan3->id);
    1314          $this->assertEquals((array)$plan3, external::read_plan($plan3->id));
    1315  
    1316          $this->setUser($this->user);
    1317  
    1318          // The normal user can not edit these plans.
    1319          $plan1->canmanage = false;
    1320          $plan2->canmanage = false;
    1321          $plan3->canmanage = false;
    1322          $plan1->canbeedited = false;
    1323          $plan2->canbeedited = false;
    1324          $plan3->canbeedited = false;
    1325          $plan1->canrequestreview = true;
    1326          $plan2->canrequestreview = true;
    1327          $plan3->canrequestreview = true;
    1328          $plan1->canreview = false;
    1329          $plan2->canreview = false;
    1330          $plan3->canreview = false;
    1331          $plan1->iscompleteallowed = false;
    1332          $plan2->iscompleteallowed = false;
    1333          $plan3->iscompleteallowed = false;
    1334          $plan1->isrequestreviewallowed = true;
    1335          $plan2->isrequestreviewallowed = true;
    1336          $plan3->isrequestreviewallowed = true;
    1337          $plan1->isapproveallowed = false;
    1338          $plan2->isapproveallowed = false;
    1339          $plan3->isapproveallowed = false;
    1340          $plan1->isunapproveallowed = false;
    1341          $plan2->isunapproveallowed = false;
    1342          $plan3->isunapproveallowed = false;
    1343          $plan3->isreopenallowed = false;
    1344          $plan1->commentarea['canpost'] = false;
    1345          $plan1->commentarea['canview'] = true;
    1346  
    1347          // Prevent the user from seeing their own non-draft plans.
    1348          assign_capability('moodle/competency:plancommentown', CAP_PROHIBIT, $this->userrole, $syscontext->id, true);
    1349          assign_capability('moodle/competency:planviewown', CAP_PROHIBIT, $this->userrole, $syscontext->id, true);
    1350          assign_capability('moodle/competency:planviewowndraft', CAP_ALLOW, $this->userrole, $syscontext->id, true);
    1351          accesslib_clear_all_caches_for_unit_testing();
    1352  
    1353          $this->assertEquals((array)$plan1, external::read_plan($plan1->id));
    1354  
    1355          try {
    1356              external::read_plan($plan2->id);
    1357              $this->fail('Exception expected due to not permissions to read plan');
    1358          } catch (\moodle_exception $e) {
    1359              $this->assertEquals('nopermissions', $e->errorcode);
    1360          }
    1361          try {
    1362              external::read_plan($plan3->id);
    1363              $this->fail('Exception expected due to not permissions to read plan');
    1364          } catch (\moodle_exception $e) {
    1365              $this->assertEquals('nopermissions', $e->errorcode);
    1366          }
    1367  
    1368          // Allow user to see their plan.
    1369          assign_capability('moodle/competency:plancommentown', CAP_ALLOW, $this->userrole, $syscontext->id, true);
    1370          assign_capability('moodle/competency:planviewown', CAP_ALLOW, $this->userrole, $syscontext->id, true);
    1371          assign_capability('moodle/competency:planmanageowndraft', CAP_PROHIBIT, $this->userrole, $syscontext->id, true);
    1372          accesslib_clear_all_caches_for_unit_testing();
    1373  
    1374          $plan1->commentarea['canpost'] = true;
    1375          $plan1->commentarea['canview'] = true;
    1376          $plan2->commentarea['canpost'] = true;
    1377          $plan2->isrequestreviewallowed = false;
    1378          $plan3->commentarea['canpost'] = true;
    1379          $plan3->isrequestreviewallowed = false;
    1380          $plan1->commentarea['canpostorhascomments'] = true;
    1381          $plan2->commentarea['canpostorhascomments'] = true;
    1382          $plan3->commentarea['canpostorhascomments'] = true;
    1383  
    1384          $this->assertEquals((array)$plan1, external::read_plan($plan1->id));
    1385          $this->assertEquals((array)$plan2, external::read_plan($plan2->id));
    1386          $this->assertEquals((array)$plan3, external::read_plan($plan3->id));
    1387  
    1388          // Allow use to manage their own draft plan.
    1389          assign_capability('moodle/competency:planviewown', CAP_PROHIBIT, $this->userrole, $syscontext->id, true);
    1390          assign_capability('moodle/competency:planmanageown', CAP_PROHIBIT, $this->userrole, $syscontext->id, true);
    1391          assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $this->userrole, $syscontext->id, true);
    1392          accesslib_clear_all_caches_for_unit_testing();
    1393  
    1394          $plan1->canmanage = true;
    1395          $plan1->canbeedited = true;
    1396          $plan1->canrequestreview = true;
    1397          $plan1->isrequestreviewallowed = true;
    1398          $this->assertEquals((array)$plan1, external::read_plan($plan1->id));
    1399          try {
    1400              external::read_plan($plan2->id);
    1401              $this->fail('Exception expected due to not permissions to read plan');
    1402          } catch (\moodle_exception $e) {
    1403              $this->assertEquals('nopermissions', $e->errorcode);
    1404          }
    1405          try {
    1406              external::read_plan($plan3->id);
    1407              $this->fail('Exception expected due to not permissions to read plan');
    1408          } catch (\moodle_exception $e) {
    1409              $this->assertEquals('nopermissions', $e->errorcode);
    1410          }
    1411  
    1412          // Allow use to manage their plan.
    1413          assign_capability('moodle/competency:planviewown', CAP_PROHIBIT, $this->userrole, $syscontext->id, true);
    1414          assign_capability('moodle/competency:planmanageowndraft', CAP_PROHIBIT, $this->userrole, $syscontext->id, true);
    1415          assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $this->userrole, $syscontext->id, true);
    1416          accesslib_clear_all_caches_for_unit_testing();
    1417  
    1418          $plan1->canmanage = false;
    1419          $plan1->canbeedited = false;
    1420          $plan1->canrequestreview = true;
    1421          $plan1->canreview = true;
    1422          $plan1->isrequestreviewallowed = true;
    1423          $plan1->isapproveallowed = true;
    1424          $plan1->iscompleteallowed = false;
    1425  
    1426          $plan2->canmanage = true;
    1427          $plan2->canbeedited = true;
    1428          $plan2->canreview = true;
    1429          $plan2->iscompleteallowed = true;
    1430          $plan2->isunapproveallowed = true;
    1431  
    1432          $plan3->canmanage = true;
    1433          $plan3->canreview = true;
    1434          $plan3->isreopenallowed = true;
    1435  
    1436          $this->assertEquals((array)$plan1, external::read_plan($plan1->id));
    1437          $this->assertEquals((array)$plan2, external::read_plan($plan2->id));
    1438          $this->assertEquals((array)$plan3, external::read_plan($plan3->id));
    1439      }
    1440  
    1441      public function test_delete_plans() {
    1442          $this->setUser($this->creator);
    1443  
    1444          $syscontext = \context_system::instance();
    1445  
    1446          $plan1 = $this->create_plan(1, $this->user->id, 0, plan::STATUS_ACTIVE, 0);
    1447          $plan2 = $this->create_plan(2, $this->user->id, 0, plan::STATUS_ACTIVE, 0);
    1448          $plan3 = $this->create_plan(3, $this->creator->id, 0, plan::STATUS_ACTIVE, 0);
    1449  
    1450          $this->assertTrue(external::delete_plan($plan1->id));
    1451  
    1452          unassign_capability('moodle/competency:planmanage', $this->creatorrole, $syscontext->id);
    1453          accesslib_clear_all_caches_for_unit_testing();
    1454  
    1455          try {
    1456              external::delete_plan($plan2->id);
    1457              $this->fail('Exception expected due to not permissions to manage plans');
    1458          } catch (\moodle_exception $e) {
    1459              $this->assertEquals('nopermissions', $e->errorcode);
    1460          }
    1461  
    1462          $this->setUser($this->user);
    1463  
    1464          // Can not delete plans created by other users.
    1465          try {
    1466              external::delete_plan($plan2->id);
    1467              $this->fail('Exception expected due to not permissions to manage plans');
    1468          } catch (\moodle_exception $e) {
    1469              $this->assertEquals('nopermissions', $e->errorcode);
    1470          }
    1471  
    1472          assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $this->userrole, $syscontext->id);
    1473          accesslib_clear_all_caches_for_unit_testing();
    1474  
    1475          $this->assertTrue(external::delete_plan($plan2->id));
    1476  
    1477          // Can not delete plans created for other users.
    1478          try {
    1479              external::delete_plan($plan3->id);
    1480              $this->fail('Exception expected due to not permissions to manage plans');
    1481          } catch (\moodle_exception $e) {
    1482              $this->assertEquals('nopermissions', $e->errorcode);
    1483          }
    1484  
    1485          $plan4 = $this->create_plan(4, $this->user->id, 0, plan::STATUS_ACTIVE, 0);
    1486          $this->assertTrue(external::delete_plan($plan4->id));
    1487      }
    1488  
    1489      public function test_delete_plan_removes_relations() {
    1490          $this->setAdminUser();
    1491          $dg = $this->getDataGenerator();
    1492          $lpg = $dg->get_plugin_generator('core_competency');
    1493  
    1494          $user = $dg->create_user();
    1495          $plan = $lpg->create_plan(array('userid' => $user->id));
    1496          $framework = $lpg->create_framework();
    1497          $comp1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    1498          $comp2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    1499          $comp3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    1500          $pc1 = $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $comp1->get('id')));
    1501          $pc2 = $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $comp2->get('id')));
    1502          $pc3 = $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $comp3->get('id')));
    1503  
    1504          // Complete the plan to generate user_competency_plan entries.
    1505          api::complete_plan($plan);
    1506  
    1507          // Confirm the data we have.
    1508          $this->assertEquals(3, plan_competency::count_records(array('planid' => $plan->get('id'))));
    1509          $this->assertEquals(3, user_competency_plan::count_records(array('planid' => $plan->get('id'), 'userid' => $user->id)));
    1510  
    1511          // Delete the plan now.
    1512          api::delete_plan($plan->get('id'));
    1513          $this->assertEquals(0, plan_competency::count_records(array('planid' => $plan->get('id'))));
    1514          $this->assertEquals(0, user_competency_plan::count_records(array('planid' => $plan->get('id'), 'userid' => $user->id)));
    1515      }
    1516  
    1517      public function test_list_plan_competencies() {
    1518          $this->setUser($this->creator);
    1519  
    1520          $dg = $this->getDataGenerator();
    1521          $lpg = $dg->get_plugin_generator('core_competency');
    1522  
    1523          $f1 = $lpg->create_framework();
    1524          $f2 = $lpg->create_framework();
    1525  
    1526          $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
    1527          $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
    1528          $c1c = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
    1529          $c2a = $lpg->create_competency(array('competencyframeworkid' => $f2->get('id')));
    1530          $c2b = $lpg->create_competency(array('competencyframeworkid' => $f2->get('id')));
    1531  
    1532          $tpl = $lpg->create_template();
    1533          $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $c1a->get('id')));
    1534          $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $c1c->get('id')));
    1535          $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $c2b->get('id')));
    1536  
    1537          $plan = $lpg->create_plan(array('userid' => $this->user->id, 'templateid' => $tpl->get('id')));
    1538  
    1539          $uc1a = $lpg->create_user_competency(array('userid' => $this->user->id, 'competencyid' => $c1a->get('id'),
    1540              'status' => user_competency::STATUS_IN_REVIEW, 'reviewerid' => $this->creator->id));
    1541          $uc1b = $lpg->create_user_competency(array('userid' => $this->user->id, 'competencyid' => $c1b->get('id')));
    1542          $uc2b = $lpg->create_user_competency(array('userid' => $this->user->id, 'competencyid' => $c2b->get('id'),
    1543              'grade' => 2, 'proficiency' => 1));
    1544          $ux1a = $lpg->create_user_competency(array('userid' => $this->creator->id, 'competencyid' => $c1a->get('id')));
    1545  
    1546          $result = external::list_plan_competencies($plan->get('id'));
    1547          $result = external::clean_returnvalue(external::list_plan_competencies_returns(), $result);
    1548  
    1549          $this->assertCount(3, $result);
    1550          $this->assertEquals($c1a->get('id'), $result[0]['competency']['id']);
    1551          $this->assertEquals($this->user->id, $result[0]['usercompetency']['userid']);
    1552          $this->assertArrayNotHasKey('usercompetencyplan', $result[0]);
    1553          $this->assertEquals($c1c->get('id'), $result[1]['competency']['id']);
    1554          $this->assertEquals($this->user->id, $result[1]['usercompetency']['userid']);
    1555          $this->assertArrayNotHasKey('usercompetencyplan', $result[1]);
    1556          $this->assertEquals($c2b->get('id'), $result[2]['competency']['id']);
    1557          $this->assertEquals($this->user->id, $result[2]['usercompetency']['userid']);
    1558          $this->assertArrayNotHasKey('usercompetencyplan', $result[2]);
    1559          $this->assertEquals(user_competency::STATUS_IN_REVIEW, $result[0]['usercompetency']['status']);
    1560          $this->assertEquals(null, $result[1]['usercompetency']['grade']);
    1561          $this->assertEquals(2, $result[2]['usercompetency']['grade']);
    1562          $this->assertEquals(1, $result[2]['usercompetency']['proficiency']);
    1563  
    1564          // Check the return values when the plan status is complete.
    1565          $completedplan = $lpg->create_plan(array('userid' => $this->user->id, 'templateid' => $tpl->get('id'),
    1566                  'status' => plan::STATUS_COMPLETE));
    1567  
    1568          $uc1a = $lpg->create_user_competency_plan(array('userid' => $this->user->id, 'competencyid' => $c1a->get('id'),
    1569                  'planid' => $completedplan->get('id')));
    1570          $uc1b = $lpg->create_user_competency_plan(array('userid' => $this->user->id, 'competencyid' => $c1c->get('id'),
    1571                  'planid' => $completedplan->get('id')));
    1572          $uc2b = $lpg->create_user_competency_plan(array('userid' => $this->user->id, 'competencyid' => $c2b->get('id'),
    1573                  'planid' => $completedplan->get('id'), 'grade' => 2, 'proficiency' => 1));
    1574          $ux1a = $lpg->create_user_competency_plan(array('userid' => $this->creator->id, 'competencyid' => $c1a->get('id'),
    1575                  'planid' => $completedplan->get('id')));
    1576  
    1577          $result = external::list_plan_competencies($completedplan->get('id'));
    1578          $result = external::clean_returnvalue(external::list_plan_competencies_returns(), $result);
    1579  
    1580          $this->assertCount(3, $result);
    1581          $this->assertEquals($c1a->get('id'), $result[0]['competency']['id']);
    1582          $this->assertEquals($this->user->id, $result[0]['usercompetencyplan']['userid']);
    1583          $this->assertArrayNotHasKey('usercompetency', $result[0]);
    1584          $this->assertEquals($c1c->get('id'), $result[1]['competency']['id']);
    1585          $this->assertEquals($this->user->id, $result[1]['usercompetencyplan']['userid']);
    1586          $this->assertArrayNotHasKey('usercompetency', $result[1]);
    1587          $this->assertEquals($c2b->get('id'), $result[2]['competency']['id']);
    1588          $this->assertEquals($this->user->id, $result[2]['usercompetencyplan']['userid']);
    1589          $this->assertArrayNotHasKey('usercompetency', $result[2]);
    1590          $this->assertEquals(null, $result[1]['usercompetencyplan']['grade']);
    1591          $this->assertEquals(2, $result[2]['usercompetencyplan']['grade']);
    1592          $this->assertEquals(1, $result[2]['usercompetencyplan']['proficiency']);
    1593      }
    1594  
    1595      public function test_add_competency_to_template() {
    1596          $this->setUser($this->creator);
    1597  
    1598          $syscontext = \context_system::instance();
    1599  
    1600          // Create a template.
    1601          $template = $this->create_template(1, true);
    1602  
    1603          // Create a competency.
    1604          $framework = $this->create_competency_framework(1, true);
    1605          $competency = $this->create_competency(1, $framework->id);
    1606  
    1607          // Add the competency.
    1608          external::add_competency_to_template($template->id, $competency->id);
    1609  
    1610          // Check that it was added.
    1611          $this->assertEquals(1, external::count_competencies_in_template($template->id));
    1612  
    1613          // Unassign capability.
    1614          unassign_capability('moodle/competency:templatemanage', $this->creatorrole, $syscontext->id);
    1615          accesslib_clear_all_caches_for_unit_testing();
    1616  
    1617          // Check we can not add the competency now.
    1618          try {
    1619              external::add_competency_to_template($template->id, $competency->id);
    1620              $this->fail('Exception expected due to not permissions to manage template competencies');
    1621          } catch (\moodle_exception $e) {
    1622              $this->assertEquals('nopermissions', $e->errorcode);
    1623          }
    1624      }
    1625  
    1626      public function test_remove_competency_from_template() {
    1627          $syscontext = \context_system::instance();
    1628          $this->setUser($this->creator);
    1629          $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
    1630  
    1631          // Create a template.
    1632          $template = $this->create_template(1, true);
    1633  
    1634          // Create a competency.
    1635          $framework = $lpg->create_framework();
    1636          $competency = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    1637  
    1638          // Add the competency.
    1639          external::add_competency_to_template($template->id, $competency->get('id'));
    1640  
    1641          // Check that it was added.
    1642          $this->assertEquals(1, external::count_competencies_in_template($template->id));
    1643  
    1644          // Check that we can remove the competency.
    1645          external::remove_competency_from_template($template->id, $competency->get('id'));
    1646  
    1647          // Check that it was removed.
    1648          $this->assertEquals(0, external::count_competencies_in_template($template->id));
    1649  
    1650          // Unassign capability.
    1651          unassign_capability('moodle/competency:templatemanage', $this->creatorrole, $syscontext->id);
    1652          accesslib_clear_all_caches_for_unit_testing();
    1653  
    1654          // Check we can not remove the competency now.
    1655          try {
    1656              external::add_competency_to_template($template->id, $competency->get('id'));
    1657              $this->fail('Exception expected due to not permissions to manage template competencies');
    1658          } catch (\moodle_exception $e) {
    1659              $this->assertEquals('nopermissions', $e->errorcode);
    1660          }
    1661      }
    1662  
    1663      /**
    1664       * Test we can re-order competency frameworks.
    1665       */
    1666      public function test_reorder_template_competencies() {
    1667          $this->setUser($this->creator);
    1668  
    1669          $syscontext = \context_system::instance();
    1670          $onehour = time() + 60 * 60;
    1671  
    1672          // Create a template.
    1673          $template = $this->create_template(1, true);
    1674  
    1675          // Create a competency framework.
    1676          $framework = $this->create_competency_framework(1, true);
    1677  
    1678          // Create multiple competencies.
    1679          $competency1 = $this->create_competency(1, $framework->id);
    1680          $competency2 = $this->create_competency(2, $framework->id);
    1681          $competency3 = $this->create_competency(3, $framework->id);
    1682          $competency4 = $this->create_competency(4, $framework->id);
    1683  
    1684          // Add the competencies.
    1685          external::add_competency_to_template($template->id, $competency1->id);
    1686          external::add_competency_to_template($template->id, $competency2->id);
    1687          external::add_competency_to_template($template->id, $competency3->id);
    1688          external::add_competency_to_template($template->id, $competency4->id);
    1689  
    1690          // Test if removing competency from template don't create sortorder holes.
    1691          external::remove_competency_from_template($template->id, $competency3->id);
    1692          $templcomp4 = template_competency::get_record(array(
    1693              'templateid' => $template->id,
    1694              'competencyid' => $competency4->id
    1695          ));
    1696  
    1697          $this->assertEquals(2, $templcomp4->get('sortorder'));
    1698  
    1699          // This is a move up.
    1700          external::reorder_template_competency($template->id, $competency4->id, $competency2->id);
    1701          $result = external::list_competencies_in_template($template->id);
    1702          $result = \external_api::clean_returnvalue(external::list_competencies_in_template_returns(), $result);
    1703  
    1704          $r1 = (object) $result[0];
    1705          $r2 = (object) $result[1];
    1706          $r3 = (object) $result[2];
    1707  
    1708          $this->assertEquals($competency1->id, $r1->id);
    1709          $this->assertEquals($competency4->id, $r2->id);
    1710          $this->assertEquals($competency2->id, $r3->id);
    1711  
    1712          // This is a move down.
    1713          external::reorder_template_competency($template->id, $competency1->id, $competency4->id);
    1714          $result = external::list_competencies_in_template($template->id);
    1715          $result = \external_api::clean_returnvalue(external::list_competencies_in_template_returns(), $result);
    1716  
    1717          $r1 = (object) $result[0];
    1718          $r2 = (object) $result[1];
    1719          $r3 = (object) $result[2];
    1720  
    1721          $this->assertEquals($competency4->id, $r1->id);
    1722          $this->assertEquals($competency1->id, $r2->id);
    1723          $this->assertEquals($competency2->id, $r3->id);
    1724  
    1725          $this->expectException('\required_capability_exception');
    1726          $this->setUser($this->user);
    1727          external::reorder_template_competency($template->id, $competency1->id, $competency2->id);
    1728      }
    1729  
    1730      /**
    1731       * Test we can duplicate learning plan template.
    1732       */
    1733      public function test_duplicate_learning_plan_template() {
    1734          $this->setUser($this->creator);
    1735  
    1736          $syscontext = \context_system::instance();
    1737          $onehour = time() + 60 * 60;
    1738  
    1739          // Create a template.
    1740          $template = $this->create_template(1, true);
    1741  
    1742          // Create a competency framework.
    1743          $framework = $this->create_competency_framework(1, true);
    1744  
    1745          // Create multiple competencies.
    1746          $competency1 = $this->create_competency(1, $framework->id);
    1747          $competency2 = $this->create_competency(2, $framework->id);
    1748          $competency3 = $this->create_competency(3, $framework->id);
    1749  
    1750          // Add the competencies.
    1751          external::add_competency_to_template($template->id, $competency1->id);
    1752          external::add_competency_to_template($template->id, $competency2->id);
    1753          external::add_competency_to_template($template->id, $competency3->id);
    1754  
    1755          // Duplicate the learning plan template.
    1756          $duplicatedtemplate = external::duplicate_template($template->id);
    1757  
    1758          $result = external::list_competencies_in_template($template->id);
    1759          $resultduplicated = external::list_competencies_in_template($duplicatedtemplate->id);
    1760  
    1761          $this->assertEquals(count($result), count($resultduplicated));
    1762          $this->assertStringContainsString($template->shortname, $duplicatedtemplate->shortname);
    1763          $this->assertEquals($duplicatedtemplate->description, $template->description);
    1764          $this->assertEquals($duplicatedtemplate->descriptionformat, $template->descriptionformat);
    1765          $this->assertEquals($duplicatedtemplate->visible, $template->visible);
    1766      }
    1767  
    1768      /**
    1769       * Test that we can return scale values for a scale with the scale ID.
    1770       */
    1771      public function test_get_scale_values() {
    1772          global $DB;
    1773  
    1774          $this->setUser($this->creator);
    1775  
    1776          // Create a scale.
    1777          $record = new \stdClass();
    1778          $record->courseid = 0;
    1779          $record->userid = $this->creator->id;
    1780          $record->name = 'Test scale';
    1781          $record->scale = 'Poor, Not good, Okay, Fine, Excellent';
    1782          $record->description = '<p>Test scale description.</p>';
    1783          $record->descriptionformat = 1;
    1784          $record->timemodified = time();
    1785          $scaleid = $DB->insert_record('scale', $record);
    1786          // Expected return value.
    1787          $expected = array(array(
    1788                  'id' => 1,
    1789                  'name' => 'Poor'
    1790              ), array(
    1791                  'id' => 2,
    1792                  'name' => 'Not good'
    1793              ), array(
    1794                  'id' => 3,
    1795                  'name' => 'Okay'
    1796              ), array(
    1797                  'id' => 4,
    1798                  'name' => 'Fine'
    1799              ), array(
    1800                  'id' => 5,
    1801                  'name' => 'Excellent'
    1802              )
    1803          );
    1804          // Call the webservice.
    1805          $result = external::get_scale_values($scaleid);
    1806          $this->assertEquals($expected, $result);
    1807      }
    1808  
    1809      /**
    1810       * Create a template.
    1811       */
    1812      public function test_create_template() {
    1813          $syscontextid = \context_system::instance()->id;
    1814          $catcontextid = \context_coursecat::instance($this->category->id)->id;
    1815  
    1816          // A user without permission.
    1817          $this->setUser($this->user);
    1818          try {
    1819              $result = $this->create_template(1, true);
    1820              $this->fail('Invalid permissions');
    1821          } catch (\required_capability_exception $e) {
    1822              // All good.
    1823          }
    1824  
    1825          // A user without permission in a category.
    1826          $this->setUser($this->catuser);
    1827          try {
    1828              $result = $this->create_template(1, false);
    1829              $this->fail('Invalid permissions');
    1830          } catch (\required_capability_exception $e) {
    1831              // All good.
    1832          }
    1833  
    1834          // A user with permissions in the system.
    1835          $this->setUser($this->creator);
    1836          $result = $this->create_template(1, true);
    1837          $this->assertEquals('shortname1', $result->shortname);
    1838          $this->assertEquals($syscontextid, $result->contextid);
    1839          $this->assertNotEmpty($result->id);
    1840  
    1841          $result = $this->create_template(2, false);
    1842          $this->assertEquals('shortname2', $result->shortname);
    1843          $this->assertEquals($catcontextid, $result->contextid);
    1844          $this->assertNotEmpty($result->id);
    1845  
    1846          // A user with permissions in the category.
    1847          $this->setUser($this->catcreator);
    1848          try {
    1849              $result = $this->create_template(3, true);
    1850              $this->fail('Invalid permissions');
    1851          } catch (\required_capability_exception $e) {
    1852              // All good.
    1853          }
    1854  
    1855          $result = $this->create_template(3, false);
    1856          $this->assertEquals('shortname3', $result->shortname);
    1857          $this->assertEquals($catcontextid, $result->contextid);
    1858          $this->assertNotEmpty($result->id);
    1859      }
    1860  
    1861      /**
    1862       * Read a template.
    1863       */
    1864      public function test_read_template() {
    1865          $syscontextid = \context_system::instance()->id;
    1866          $catcontextid = \context_coursecat::instance($this->category->id)->id;
    1867  
    1868          // Set a due date for the next year.
    1869          $date = new \DateTime('now');
    1870          $date->modify('+1 year');
    1871          $duedate = $date->getTimestamp();
    1872  
    1873          // Creating two templates.
    1874          $this->setUser($this->creator);
    1875          $systemplate = $this->create_template(1, true);
    1876          $cattemplate = $this->create_template(2, false);
    1877  
    1878          // User without permissions to read in system.
    1879          assign_capability('moodle/competency:templateview', CAP_PROHIBIT, $this->userrole, $syscontextid, true);
    1880          accesslib_clear_all_caches_for_unit_testing();
    1881          $this->setUser($this->user);
    1882          $this->assertFalse(has_capability('moodle/competency:templateview', \context_system::instance()));
    1883          try {
    1884              external::read_template($systemplate->id);
    1885              $this->fail('Invalid permissions');
    1886          } catch (\required_capability_exception $e) {
    1887              // All good.
    1888          }
    1889          try {
    1890              external::read_template($cattemplate->id);
    1891              $this->fail('Invalid permissions');
    1892          } catch (\required_capability_exception $e) {
    1893              // All good.
    1894          }
    1895  
    1896          // User with permissions to read in a category.
    1897          assign_capability('moodle/competency:templateview', CAP_PREVENT, $this->userrole, $syscontextid, true);
    1898          assign_capability('moodle/competency:templateview', CAP_ALLOW, $this->userrole, $catcontextid, true);
    1899          accesslib_clear_all_caches_for_unit_testing();
    1900          $this->assertFalse(has_capability('moodle/competency:templateview', \context_system::instance()));
    1901          $this->assertTrue(has_capability('moodle/competency:templateview', \context_coursecat::instance($this->category->id)));
    1902          try {
    1903              external::read_template($systemplate->id);
    1904              $this->fail('Invalid permissions');
    1905          } catch (\required_capability_exception $e) {
    1906              // All good.
    1907          }
    1908  
    1909          $result = external::read_template($cattemplate->id);
    1910          $result = \external_api::clean_returnvalue(external::read_template_returns(), $result);
    1911          $this->assertEquals($cattemplate->id, $result['id']);
    1912          $this->assertEquals('shortname2', $result['shortname']);
    1913          $this->assertEquals('description2', $result['description']);
    1914          $this->assertEquals(FORMAT_HTML, $result['descriptionformat']);
    1915          $this->assertEquals(1, $result['visible']);
    1916          $this->assertEquals(0, $result['duedate']);
    1917          $this->assertEquals(userdate(0), $result['duedateformatted']);
    1918  
    1919          // User with permissions to read in the system.
    1920          assign_capability('moodle/competency:templateview', CAP_ALLOW, $this->userrole, $syscontextid, true);
    1921          accesslib_clear_all_caches_for_unit_testing();
    1922          $this->assertTrue(has_capability('moodle/competency:templateview', \context_system::instance()));
    1923          $result = external::read_template($systemplate->id);
    1924          $result = \external_api::clean_returnvalue(external::read_template_returns(), $result);
    1925          $this->assertEquals($systemplate->id, $result['id']);
    1926          $this->assertEquals('shortname1', $result['shortname']);
    1927          $this->assertEquals('description1', $result['description']);
    1928          $this->assertEquals(FORMAT_HTML, $result['descriptionformat']);
    1929          $this->assertEquals(true, $result['visible']);
    1930          $this->assertEquals(0, $result['duedate']);
    1931          $this->assertEquals(userdate(0), $result['duedateformatted']);
    1932  
    1933          $result = external::read_template($cattemplate->id);
    1934          $result = \external_api::clean_returnvalue(external::read_template_returns(), $result);
    1935          $this->assertEquals($cattemplate->id, $result['id']);
    1936          $this->assertEquals('shortname2', $result['shortname']);
    1937          $this->assertEquals('description2', $result['description']);
    1938          $this->assertEquals(FORMAT_HTML, $result['descriptionformat']);
    1939          $this->assertEquals(true, $result['visible']);
    1940          $this->assertEquals(0, $result['duedate']);
    1941          $this->assertEquals(userdate(0), $result['duedateformatted']);
    1942      }
    1943  
    1944      /**
    1945       * Update a template.
    1946       */
    1947      public function test_update_template() {
    1948          $syscontextid = \context_system::instance()->id;
    1949          $catcontextid = \context_coursecat::instance($this->category->id)->id;
    1950  
    1951          // Set a due date for the next year.
    1952          $date = new \DateTime('now');
    1953          $date->modify('+1 year');
    1954          $duedate = $date->getTimestamp();
    1955  
    1956          // Creating two templates.
    1957          $this->setUser($this->creator);
    1958          $systemplate = $this->create_template(1, true);
    1959          $cattemplate = $this->create_template(2, false);
    1960  
    1961          // Trying to update in a without permissions.
    1962          $this->setUser($this->user);
    1963          try {
    1964              $this->update_template($systemplate->id, 3);
    1965              $this->fail('Invalid permissions');
    1966          } catch (\required_capability_exception $e) {
    1967              // All good.
    1968          }
    1969  
    1970          try {
    1971              $this->update_template($cattemplate->id, 3);
    1972              $this->fail('Invalid permissions');
    1973          } catch (\required_capability_exception $e) {
    1974              // All good.
    1975          }
    1976  
    1977          // User with permissions to update in category.
    1978          $this->setUser($this->catcreator);
    1979          try {
    1980              $this->update_template($systemplate->id, 3);
    1981              $this->fail('Invalid permissions');
    1982          } catch (\required_capability_exception $e) {
    1983              // All good.
    1984          }
    1985  
    1986          $result = $this->update_template($cattemplate->id, 3);
    1987          $this->assertTrue($result);
    1988          $result = external::read_template($cattemplate->id);
    1989          $result = \external_api::clean_returnvalue(external::read_template_returns(), $result);
    1990          $this->assertEquals($cattemplate->id, $result['id']);
    1991          $this->assertEquals('shortname3', $result['shortname']);
    1992          $this->assertEquals("description3", $result['description']);
    1993          $this->assertEquals(FORMAT_HTML, $result['descriptionformat']);
    1994          $this->assertEquals(true, $result['visible']);
    1995          $this->assertEquals(0, $result['duedate']);
    1996          $this->assertEquals(userdate(0), $result['duedateformatted']);
    1997  
    1998          // User with permissions to update in the system.
    1999          $this->setUser($this->creator);
    2000          $result = $this->update_template($systemplate->id, 4);
    2001          $this->assertTrue($result);
    2002          $result = external::read_template($systemplate->id);
    2003          $result = \external_api::clean_returnvalue(external::read_template_returns(), $result);
    2004          $this->assertEquals($systemplate->id, $result['id']);
    2005          $this->assertEquals('shortname4', $result['shortname']);
    2006          $this->assertEquals('description4', $result['description']);
    2007          $this->assertEquals(FORMAT_HTML, $result['descriptionformat']);
    2008          $this->assertEquals(true, $result['visible']);
    2009          $this->assertEquals(0, $result['duedate']);
    2010          $this->assertEquals(userdate(0), $result['duedateformatted']);
    2011  
    2012          $result = $this->update_template($cattemplate->id, 5);
    2013          $this->assertTrue($result);
    2014          $result = external::read_template($cattemplate->id);
    2015          $result = \external_api::clean_returnvalue(external::read_template_returns(), $result);
    2016          $this->assertEquals($cattemplate->id, $result['id']);
    2017          $this->assertEquals('shortname5', $result['shortname']);
    2018          $this->assertEquals('description5', $result['description']);
    2019          $this->assertEquals(FORMAT_HTML, $result['descriptionformat']);
    2020          $this->assertEquals(1, $result['visible']);
    2021          $this->assertEquals(0, $result['duedate']);
    2022          $this->assertEquals(userdate(0), $result['duedateformatted']);
    2023      }
    2024  
    2025      /**
    2026       * Delete a template.
    2027       */
    2028      public function test_delete_template() {
    2029          global $DB;
    2030          $syscontextid = \context_system::instance()->id;
    2031          $catcontextid = \context_coursecat::instance($this->category->id)->id;
    2032  
    2033          // Creating a few templates.
    2034          $this->setUser($this->creator);
    2035          $sys1 = $this->create_template(1, true);
    2036          $cat1 = $this->create_template(2, false);
    2037          $cat2 = $this->create_template(3, false);
    2038          $this->assertTrue($DB->record_exists(template::TABLE, array('id' => $sys1->id)));
    2039          $this->assertTrue($DB->record_exists(template::TABLE, array('id' => $cat1->id)));
    2040          $this->assertTrue($DB->record_exists(template::TABLE, array('id' => $cat2->id)));
    2041  
    2042          // User without permissions.
    2043          $this->setUser($this->user);
    2044          try {
    2045              external::delete_template($sys1->id);
    2046              $this->fail('Invalid permissions');
    2047          } catch (\required_capability_exception $e) {
    2048              // All good.
    2049          }
    2050          try {
    2051              external::delete_template($cat1->id);
    2052              $this->fail('Invalid permissions');
    2053          } catch (\required_capability_exception $e) {
    2054              // All good.
    2055          }
    2056  
    2057          // User with category permissions.
    2058          $this->setUser($this->catcreator);
    2059          try {
    2060              external::delete_template($sys1->id);
    2061              $this->fail('Invalid permissions');
    2062          } catch (\required_capability_exception $e) {
    2063              // All good.
    2064          }
    2065  
    2066          $result = external::delete_template($cat1->id);
    2067          $result = \external_api::clean_returnvalue(external::delete_template_returns(), $result);
    2068          $this->assertTrue($result);
    2069          $this->assertFalse($DB->record_exists(template::TABLE, array('id' => $cat1->id)));
    2070  
    2071          // User with system permissions.
    2072          $this->setUser($this->creator);
    2073          $result = external::delete_template($sys1->id);
    2074          $result = \external_api::clean_returnvalue(external::delete_template_returns(), $result);
    2075          $this->assertTrue($result);
    2076          $result = external::delete_template($cat2->id);
    2077          $result = \external_api::clean_returnvalue(external::delete_template_returns(), $result);
    2078          $this->assertTrue($result);
    2079          $this->assertFalse($DB->record_exists(template::TABLE, array('id' => $sys1->id)));
    2080          $this->assertFalse($DB->record_exists(template::TABLE, array('id' => $cat2->id)));
    2081      }
    2082  
    2083      /**
    2084       * List templates.
    2085       */
    2086      public function test_list_templates() {
    2087          $syscontextid = \context_system::instance()->id;
    2088          $catcontextid = \context_coursecat::instance($this->category->id)->id;
    2089  
    2090          // Creating a few templates.
    2091          $this->setUser($this->creator);
    2092          $sys1 = $this->create_template(1, true);
    2093          $sys2 = $this->create_template(2, true);
    2094          $cat1 = $this->create_template(3, false);
    2095          $cat2 = $this->create_template(4, false);
    2096  
    2097          // User without permission.
    2098          $this->setUser($this->user);
    2099          assign_capability('moodle/competency:templateview', CAP_PROHIBIT, $this->userrole, $syscontextid, true);
    2100          accesslib_clear_all_caches_for_unit_testing();
    2101          try {
    2102              external::list_templates('id', 'ASC', 0, 10, array('contextid' => $syscontextid), 'children', false);
    2103              $this->fail('Invalid permissions');
    2104          } catch (\required_capability_exception $e) {
    2105              // All good.
    2106          }
    2107  
    2108          // User with category permissions.
    2109          assign_capability('moodle/competency:templateview', CAP_PREVENT, $this->userrole, $syscontextid, true);
    2110          assign_capability('moodle/competency:templateview', CAP_ALLOW, $this->userrole, $catcontextid, true);
    2111          accesslib_clear_all_caches_for_unit_testing();
    2112          $result = external::list_templates('id', 'ASC', 0, 10, array('contextid' => $syscontextid), 'children', false);
    2113          $result = \external_api::clean_returnvalue(external::list_templates_returns(), $result);
    2114          $this->assertCount(2, $result);
    2115          $this->assertEquals($cat1->id, $result[0]['id']);
    2116          $this->assertEquals($cat2->id, $result[1]['id']);
    2117  
    2118          // User with system permissions.
    2119          assign_capability('moodle/competency:templateview', CAP_ALLOW, $this->userrole, $syscontextid, true);
    2120          accesslib_clear_all_caches_for_unit_testing();
    2121          $result = external::list_templates('id', 'DESC', 0, 3, array('contextid' => $catcontextid), 'parents', false);
    2122          $result = \external_api::clean_returnvalue(external::list_templates_returns(), $result);
    2123          $this->assertCount(3, $result);
    2124          $this->assertEquals($cat2->id, $result[0]['id']);
    2125          $this->assertEquals($cat1->id, $result[1]['id']);
    2126          $this->assertEquals($sys2->id, $result[2]['id']);
    2127      }
    2128  
    2129      /**
    2130       * List templates using competency.
    2131       */
    2132      public function test_list_templates_using_competency() {
    2133          $this->setUser($this->creator);
    2134  
    2135          // Create a template.
    2136          $template1 = $this->create_template(1, true);
    2137          $template2 = $this->create_template(2, true);
    2138          $template3 = $this->create_template(3, true);
    2139          $template4 = $this->create_template(4, true);
    2140  
    2141          // Create a competency.
    2142          $framework = $this->create_competency_framework(1, true);
    2143          $competency1 = $this->create_competency(1, $framework->id);
    2144          $competency2 = $this->create_competency(2, $framework->id);
    2145  
    2146          // Add the competency.
    2147          external::add_competency_to_template($template1->id, $competency1->id);
    2148          external::add_competency_to_template($template2->id, $competency1->id);
    2149          external::add_competency_to_template($template3->id, $competency1->id);
    2150  
    2151          external::add_competency_to_template($template4->id, $competency2->id);
    2152  
    2153          $listcomp1 = external::list_templates_using_competency($competency1->id);
    2154          $listcomp2 = external::list_templates_using_competency($competency2->id);
    2155  
    2156          // Test count_templates_using_competency.
    2157          $counttempcomp1 = external::count_templates_using_competency($competency1->id);
    2158          $counttempcomp2 = external::count_templates_using_competency($competency2->id);
    2159  
    2160          $comptemp1 = $listcomp1[0];
    2161          $comptemp2 = $listcomp1[1];
    2162          $comptemp3 = $listcomp1[2];
    2163  
    2164          $comptemp4 = $listcomp2[0];
    2165  
    2166          $this->assertCount(3, $listcomp1);
    2167          $this->assertCount(1, $listcomp2);
    2168          $this->assertEquals(3, $counttempcomp1);
    2169          $this->assertEquals(1, $counttempcomp2);
    2170          $this->assertEquals($template1->id, $comptemp1->id);
    2171          $this->assertEquals($template2->id, $comptemp2->id);
    2172          $this->assertEquals($template3->id, $comptemp3->id);
    2173          $this->assertEquals($template4->id, $comptemp4->id);
    2174      }
    2175  
    2176      public function test_count_templates() {
    2177          $syscontextid = \context_system::instance()->id;
    2178          $catcontextid = \context_coursecat::instance($this->category->id)->id;
    2179  
    2180          // Creating a few templates.
    2181          $this->setUser($this->creator);
    2182          $sys1 = $this->create_template(1, true);
    2183          $sys2 = $this->create_template(2, true);
    2184          $cat1 = $this->create_template(3, false);
    2185          $cat2 = $this->create_template(4, false);
    2186          $cat3 = $this->create_template(5, false);
    2187  
    2188          // User without permission.
    2189          $this->setUser($this->user);
    2190          assign_capability('moodle/competency:templateview', CAP_PROHIBIT, $this->userrole, $syscontextid, true);
    2191          accesslib_clear_all_caches_for_unit_testing();
    2192          try {
    2193              external::count_templates(array('contextid' => $syscontextid), 'children');
    2194              $this->fail('Invalid permissions');
    2195          } catch (\required_capability_exception $e) {
    2196              // All good.
    2197          }
    2198  
    2199          // User with category permissions.
    2200          assign_capability('moodle/competency:templateview', CAP_PREVENT, $this->userrole, $syscontextid, true);
    2201          assign_capability('moodle/competency:templateview', CAP_ALLOW, $this->userrole, $catcontextid, true);
    2202          accesslib_clear_all_caches_for_unit_testing();
    2203          $result = external::count_templates(array('contextid' => $syscontextid), 'children');
    2204          $result = \external_api::clean_returnvalue(external::count_templates_returns(), $result);
    2205          $this->assertEquals(3, $result);
    2206  
    2207          // User with system permissions.
    2208          assign_capability('moodle/competency:templateview', CAP_ALLOW, $this->userrole, $syscontextid, true);
    2209          accesslib_clear_all_caches_for_unit_testing();
    2210          $result = external::count_templates(array('contextid' => $catcontextid), 'parents');
    2211          $result = \external_api::clean_returnvalue(external::count_templates_returns(), $result);
    2212          $this->assertEquals(5, $result);
    2213      }
    2214  
    2215      /**
    2216       * Test that we can add related competencies.
    2217       *
    2218       * @return void
    2219       */
    2220      public function test_add_related_competency() {
    2221          global $DB;
    2222          $this->setUser($this->creator);
    2223  
    2224          $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
    2225          $framework = $lpg->create_framework();
    2226          $framework2 = $lpg->create_framework();
    2227          $competency1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    2228          $competency2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    2229          $competency3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    2230          $competency4 = $lpg->create_competency(array('competencyframeworkid' => $framework2->get('id')));
    2231  
    2232          // The lower one always as competencyid.
    2233          $result = external::add_related_competency($competency1->get('id'), $competency2->get('id'));
    2234          $result = \external_api::clean_returnvalue(external::add_related_competency_returns(), $result);
    2235          $this->assertTrue($result);
    2236          $this->assertTrue($DB->record_exists_select(
    2237              related_competency::TABLE, 'competencyid = :cid AND relatedcompetencyid = :rid',
    2238              array(
    2239                  'cid' => $competency1->get('id'),
    2240                  'rid' => $competency2->get('id')
    2241              )
    2242          ));
    2243          $this->assertFalse($DB->record_exists_select(
    2244              related_competency::TABLE, 'competencyid = :cid AND relatedcompetencyid = :rid',
    2245              array(
    2246                  'cid' => $competency2->get('id'),
    2247                  'rid' => $competency1->get('id')
    2248              )
    2249          ));
    2250  
    2251          $result = external::add_related_competency($competency3->get('id'), $competency1->get('id'));
    2252          $result = \external_api::clean_returnvalue(external::add_related_competency_returns(), $result);
    2253          $this->assertTrue($result);
    2254          $this->assertTrue($DB->record_exists_select(
    2255              related_competency::TABLE, 'competencyid = :cid AND relatedcompetencyid = :rid',
    2256              array(
    2257                  'cid' => $competency1->get('id'),
    2258                  'rid' => $competency3->get('id')
    2259              )
    2260          ));
    2261          $this->assertFalse($DB->record_exists_select(
    2262              related_competency::TABLE, 'competencyid = :cid AND relatedcompetencyid = :rid',
    2263              array(
    2264                  'cid' => $competency3->get('id'),
    2265                  'rid' => $competency1->get('id')
    2266              )
    2267          ));
    2268  
    2269          // We can not allow a duplicate relation, not even in the other direction.
    2270          $this->assertEquals(1, $DB->count_records_select(related_competency::TABLE,
    2271              'competencyid = :cid AND relatedcompetencyid = :rid',
    2272              array('cid' => $competency1->get('id'), 'rid' => $competency2->get('id'))));
    2273          $this->assertEquals(0, $DB->count_records_select(related_competency::TABLE,
    2274              'competencyid = :cid AND relatedcompetencyid = :rid',
    2275              array('rid' => $competency1->get('id'), 'cid' => $competency2->get('id'))));
    2276          $result = external::add_related_competency($competency2->get('id'), $competency1->get('id'));
    2277          $result = \external_api::clean_returnvalue(external::add_related_competency_returns(), $result);
    2278          $this->assertTrue($result);
    2279          $this->assertEquals(1, $DB->count_records_select(related_competency::TABLE,
    2280              'competencyid = :cid AND relatedcompetencyid = :rid',
    2281              array('cid' => $competency1->get('id'), 'rid' => $competency2->get('id'))));
    2282          $this->assertEquals(0, $DB->count_records_select(related_competency::TABLE,
    2283              'competencyid = :cid AND relatedcompetencyid = :rid',
    2284              array('rid' => $competency1->get('id'), 'cid' => $competency2->get('id'))));
    2285  
    2286          // Check that we cannot create links across frameworks.
    2287          try {
    2288              external::add_related_competency($competency1->get('id'), $competency4->get('id'));
    2289              $this->fail('Exception expected due mis-use of shared competencies');
    2290          } catch (invalid_persistent_exception $e) {
    2291              // Yay!
    2292          }
    2293  
    2294          // User without permission.
    2295          $this->setUser($this->user);
    2296  
    2297          // Check we can not add the related competency now.
    2298          try {
    2299              external::add_related_competency($competency1->get('id'), $competency3->get('id'));
    2300              $this->fail('Exception expected due to not permissions to manage template competencies');
    2301          } catch (\moodle_exception $e) {
    2302              $this->assertEquals('nopermissions', $e->errorcode);
    2303          }
    2304  
    2305      }
    2306  
    2307      /**
    2308       * Test that we can remove related competencies.
    2309       *
    2310       * @return void
    2311       */
    2312      public function test_remove_related_competency() {
    2313          $this->setUser($this->creator);
    2314  
    2315          $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
    2316          $framework = $lpg->create_framework();
    2317          $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    2318          $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    2319          $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    2320          $rc1 = $lpg->create_related_competency(array('competencyid' => $c1->get('id'), 'relatedcompetencyid' => $c2->get('id')));
    2321          $rc2 = $lpg->create_related_competency(array('competencyid' => $c2->get('id'), 'relatedcompetencyid' => $c3->get('id')));
    2322  
    2323          $this->assertEquals(2, related_competency::count_records());
    2324  
    2325          // Returns false when the relation does not exist.
    2326          $result = external::remove_related_competency($c1->get('id'), $c3->get('id'));
    2327          $result = \external_api::clean_returnvalue(external::remove_related_competency_returns(), $result);
    2328          $this->assertFalse($result);
    2329  
    2330          // Returns true on success.
    2331          $result = external::remove_related_competency($c2->get('id'), $c3->get('id'));
    2332          $result = \external_api::clean_returnvalue(external::remove_related_competency_returns(), $result);
    2333          $this->assertTrue($result);
    2334          $this->assertEquals(1, related_competency::count_records());
    2335  
    2336          // We don't need to specify competencyid and relatedcompetencyid in the right order.
    2337          $result = external::remove_related_competency($c2->get('id'), $c1->get('id'));
    2338          $result = \external_api::clean_returnvalue(external::remove_related_competency_returns(), $result);
    2339          $this->assertTrue($result);
    2340          $this->assertEquals(0, related_competency::count_records());
    2341      }
    2342  
    2343      /**
    2344       * Test that we can search and include related competencies.
    2345       *
    2346       * @return void
    2347       */
    2348      public function test_search_competencies_including_related() {
    2349          $this->setUser($this->creator);
    2350  
    2351          $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
    2352          $framework = $lpg->create_framework();
    2353          $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    2354          $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    2355          $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    2356          $c4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    2357          $c5 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
    2358  
    2359          // We have 1-2, 1-3, 2-4, and no relation between 2-3 nor 1-4 nor 5.
    2360          $rc12 = $lpg->create_related_competency(array('competencyid' => $c1->get('id'), 'relatedcompetencyid' => $c2->get('id')));
    2361          $rc13 = $lpg->create_related_competency(array('competencyid' => $c1->get('id'), 'relatedcompetencyid' => $c3->get('id')));
    2362          $rc24 = $lpg->create_related_competency(array('competencyid' => $c2->get('id'), 'relatedcompetencyid' => $c4->get('id')));
    2363  
    2364          $result = external::search_competencies('comp', $framework->get('id'), true);
    2365          $result = \external_api::clean_returnvalue(external::search_competencies_returns(), $result);
    2366  
    2367          $this->assertCount(5, $result);
    2368  
    2369      }
    2370  
    2371      /**
    2372       * Test that we can add competency to plan if we have the right capability.
    2373       *
    2374       * @return void
    2375       */
    2376      public function test_add_competency_to_plan() {
    2377          $this->resetAfterTest(true);
    2378          $dg = $this->getDataGenerator();
    2379          $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
    2380          $usermanage = $dg->create_user();
    2381          $user = $dg->create_user();
    2382  
    2383          $syscontext = \context_system::instance();
    2384  
    2385          // Creating specific roles.
    2386          $managerole = $dg->create_role(array(
    2387              'name' => 'User manage',
    2388              'shortname' => 'manage'
    2389          ));
    2390  
    2391          assign_capability('moodle/competency:planmanage', CAP_ALLOW, $managerole, $syscontext->id);
    2392          assign_capability('moodle/competency:planview', CAP_ALLOW, $managerole, $syscontext->id);
    2393  
    2394          $dg->role_assign($managerole, $usermanage->id, $syscontext->id);
    2395  
    2396          $this->setUser($usermanage);
    2397          $plan = array (
    2398              'userid' => $usermanage->id,
    2399              'status' => \core_competency\plan::STATUS_ACTIVE
    2400          );
    2401          $pl1 = $lpg->create_plan($plan);
    2402          $framework = $lpg->create_framework();
    2403          $competency = $lpg->create_competency(
    2404                  array('competencyframeworkid' => $framework->get('id'))
    2405                  );
    2406          $this->assertTrue(external::add_competency_to_plan($pl1->get('id'), $competency->get('id')));
    2407  
    2408          // A competency cannot be added to plan based on template.
    2409          $template = $lpg->create_template();
    2410          $plan = array (
    2411              'userid' => $usermanage->id,
    2412              'status' => \core_competency\plan::STATUS_ACTIVE,
    2413              'templateid' => $template->get('id')
    2414          );
    2415          $pl2 = $lpg->create_plan($plan);
    2416          try {
    2417              external::add_competency_to_plan($pl2->get('id'), $competency->get('id'));
    2418              $this->fail('A competency cannot be added to plan based on template');
    2419          } catch (\coding_exception $ex) {
    2420              $this->assertTrue(true);
    2421          }
    2422  
    2423          // User without capability cannot add competency to a plan.
    2424</