Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

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

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Workshop module external functions tests
  19   *
  20   * @package    mod_workshop
  21   * @category   external
  22   * @copyright  2017 Juan Leyva <juan@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   * @since      Moodle 3.4
  25   */
  26  
  27  namespace mod_workshop\external;
  28  
  29  use externallib_advanced_testcase;
  30  use workshop;
  31  use mod_workshop_external;
  32  use mod_workshop\external\workshop_summary_exporter;
  33  use mod_workshop\external\submission_exporter;
  34  
  35  defined('MOODLE_INTERNAL') || die();
  36  
  37  global $CFG;
  38  
  39  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  40  require_once($CFG->dirroot . '/mod/workshop/lib.php');
  41  
  42  /**
  43   * Workshop module external functions tests
  44   *
  45   * @package    mod_workshop
  46   * @category   external
  47   * @copyright  2017 Juan Leyva <juan@moodle.com>
  48   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  49   * @since      Moodle 3.4
  50   */
  51  class external_test extends externallib_advanced_testcase {
  52  
  53      /** @var stdClass course object */
  54      private $course;
  55      /** @var stdClass workshop object */
  56      private $workshop;
  57      /** @var stdClass context object */
  58      private $context;
  59      /** @var stdClass cm object */
  60      private $cm;
  61      /** @var stdClass student object */
  62      private $student;
  63      /** @var stdClass teacher object */
  64      private $teacher;
  65      /** @var stdClass student role object */
  66      private $studentrole;
  67      /** @var stdClass teacher role object */
  68      private $teacherrole;
  69  
  70      /**
  71       * Set up for every test
  72       */
  73      public function setUp(): void {
  74          global $DB;
  75          $this->resetAfterTest();
  76          $this->setAdminUser();
  77  
  78          // Setup test data.
  79          $course = new \stdClass();
  80          $course->groupmode = SEPARATEGROUPS;
  81          $course->groupmodeforce = true;
  82          $this->course = $this->getDataGenerator()->create_course($course);
  83          $this->workshop = $this->getDataGenerator()->create_module('workshop',
  84              array(
  85                  'course' => $this->course->id,
  86                  'overallfeedbackfiles' => 1,
  87              )
  88          );
  89          $this->context = \context_module::instance($this->workshop->cmid);
  90          $this->cm = get_coursemodule_from_instance('workshop', $this->workshop->id);
  91  
  92          // Add grading strategy data (accumulative is the default).
  93          $workshop = new workshop($this->workshop, $this->cm, $this->course);
  94          $strategy = $workshop->grading_strategy_instance();
  95          $data = array();
  96          for ($i = 0; $i < 4; $i++) {
  97              $data['dimensionid__idx_'.$i] = 0;
  98              $data['description__idx_'.$i.'_editor'] = array('text' => "Content $i", 'format' => FORMAT_MOODLE);
  99              $data['grade__idx_'.$i] = 25;
 100              $data['weight__idx_'.$i] = 25;
 101          }
 102          $data['workshopid'] = $workshop->id;
 103          $data['norepeats'] = 4;
 104          $strategy->save_edit_strategy_form((object) $data);
 105  
 106          // Create users.
 107          $this->student = self::getDataGenerator()->create_user();
 108          $this->anotherstudentg1 = self::getDataGenerator()->create_user();
 109          $this->anotherstudentg2 = self::getDataGenerator()->create_user();
 110          $this->teacher = self::getDataGenerator()->create_user();
 111  
 112          // Users enrolments.
 113          $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
 114          $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
 115          $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual');
 116          $this->getDataGenerator()->enrol_user($this->anotherstudentg1->id, $this->course->id, $this->studentrole->id, 'manual');
 117          $this->getDataGenerator()->enrol_user($this->anotherstudentg2->id, $this->course->id, $this->studentrole->id, 'manual');
 118          $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');
 119  
 120          $this->group1 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
 121          $this->group2 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
 122          groups_add_member($this->group1, $this->student);
 123          groups_add_member($this->group1, $this->anotherstudentg1);
 124          groups_add_member($this->group2, $this->anotherstudentg2);
 125      }
 126  
 127      /**
 128       * Test test_mod_workshop_get_workshops_by_courses
 129       */
 130      public function test_mod_workshop_get_workshops_by_courses() {
 131  
 132          // Create additional course.
 133          $course2 = self::getDataGenerator()->create_course();
 134  
 135          // Second workshop.
 136          $record = new \stdClass();
 137          $record->course = $course2->id;
 138          $workshop2 = self::getDataGenerator()->create_module('workshop', $record);
 139  
 140          // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
 141          $enrol = enrol_get_plugin('manual');
 142          $enrolinstances = enrol_get_instances($course2->id, true);
 143          foreach ($enrolinstances as $courseenrolinstance) {
 144              if ($courseenrolinstance->enrol == "manual") {
 145                  $instance2 = $courseenrolinstance;
 146                  break;
 147              }
 148          }
 149          $enrol->enrol_user($instance2, $this->student->id, $this->studentrole->id);
 150  
 151          self::setUser($this->student);
 152  
 153          $returndescription = mod_workshop_external::get_workshops_by_courses_returns();
 154  
 155          // Create what we expect to be returned when querying the two courses.
 156          $properties = workshop_summary_exporter::read_properties_definition();
 157          $expectedfields = array_keys($properties);
 158  
 159          // Add expected coursemodule and data.
 160          $workshop1 = $this->workshop;
 161          $workshop1->coursemodule = $workshop1->cmid;
 162          $workshop1->introformat = 1;
 163          $workshop1->introfiles = [];
 164          $workshop1->lang = '';
 165          $workshop1->instructauthorsfiles = [];
 166          $workshop1->instructauthorsformat = 1;
 167          $workshop1->instructreviewersfiles = [];
 168          $workshop1->instructreviewersformat = 1;
 169          $workshop1->conclusionfiles = [];
 170          $workshop1->conclusionformat = 1;
 171          $workshop1->submissiontypetext = 1;
 172          $workshop1->submissiontypefile = 1;
 173  
 174          $workshop2->coursemodule = $workshop2->cmid;
 175          $workshop2->introformat = 1;
 176          $workshop2->introfiles = [];
 177          $workshop2->lang = '';
 178          $workshop2->instructauthorsfiles = [];
 179          $workshop2->instructauthorsformat = 1;
 180          $workshop2->instructreviewersfiles = [];
 181          $workshop2->instructreviewersformat = 1;
 182          $workshop2->conclusionfiles = [];
 183          $workshop2->conclusionformat = 1;
 184          $workshop2->submissiontypetext = 1;
 185          $workshop2->submissiontypefile = 1;
 186  
 187          foreach ($expectedfields as $field) {
 188              if (!empty($properties[$field]) && $properties[$field]['type'] == PARAM_BOOL) {
 189                  $workshop1->{$field} = (bool) $workshop1->{$field};
 190                  $workshop2->{$field} = (bool) $workshop2->{$field};
 191              }
 192              $expected1[$field] = $workshop1->{$field};
 193              $expected2[$field] = $workshop2->{$field};
 194          }
 195  
 196          $expectedworkshops = array($expected2, $expected1);
 197  
 198          // Call the external function passing course ids.
 199          $result = mod_workshop_external::get_workshops_by_courses(array($course2->id, $this->course->id));
 200          $result = \external_api::clean_returnvalue($returndescription, $result);
 201  
 202          $this->assertEquals($expectedworkshops, $result['workshops']);
 203          $this->assertCount(0, $result['warnings']);
 204  
 205          // Call the external function without passing course id.
 206          $result = mod_workshop_external::get_workshops_by_courses();
 207          $result = \external_api::clean_returnvalue($returndescription, $result);
 208          $this->assertEquals($expectedworkshops, $result['workshops']);
 209          $this->assertCount(0, $result['warnings']);
 210  
 211          // Unenrol user from second course and alter expected workshops.
 212          $enrol->unenrol_user($instance2, $this->student->id);
 213          array_shift($expectedworkshops);
 214  
 215          // Call the external function without passing course id.
 216          $result = mod_workshop_external::get_workshops_by_courses();
 217          $result = \external_api::clean_returnvalue($returndescription, $result);
 218          $this->assertEquals($expectedworkshops, $result['workshops']);
 219  
 220          // Call for the second course we unenrolled the user from, expected warning.
 221          $result = mod_workshop_external::get_workshops_by_courses(array($course2->id));
 222          $this->assertCount(1, $result['warnings']);
 223          $this->assertEquals('1', $result['warnings'][0]['warningcode']);
 224          $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
 225      }
 226  
 227      /**
 228       * Test mod_workshop_get_workshop_access_information for students.
 229       */
 230      public function test_mod_workshop_get_workshop_access_information_student() {
 231  
 232          self::setUser($this->student);
 233          $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
 234          $result = \external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
 235          // Check default values for capabilities.
 236          $enabledcaps = array('canpeerassess', 'cansubmit', 'canview', 'canviewauthornames', 'canviewauthorpublished',
 237              'canviewpublishedsubmissions', 'canexportsubmissions');
 238  
 239          foreach ($result as $capname => $capvalue) {
 240              if (strpos($capname, 'can') !== 0) {
 241                  continue;
 242              }
 243              if (in_array($capname, $enabledcaps)) {
 244                  $this->assertTrue($capvalue);
 245              } else {
 246                  $this->assertFalse($capvalue);
 247              }
 248          }
 249          // Now, unassign some capabilities.
 250          unassign_capability('mod/workshop:peerassess', $this->studentrole->id);
 251          unassign_capability('mod/workshop:submit', $this->studentrole->id);
 252          unset($enabledcaps[0]);
 253          unset($enabledcaps[1]);
 254          accesslib_clear_all_caches_for_unit_testing();
 255  
 256          $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
 257          $result = \external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
 258          foreach ($result as $capname => $capvalue) {
 259              if (strpos($capname, 'can') !== 0) {
 260                  continue;
 261              }
 262              if (in_array($capname, $enabledcaps)) {
 263                  $this->assertTrue($capvalue);
 264              } else {
 265                  $this->assertFalse($capvalue);
 266              }
 267          }
 268  
 269          // Now, specific functionalities.
 270          $this->assertFalse($result['creatingsubmissionallowed']);
 271          $this->assertFalse($result['modifyingsubmissionallowed']);
 272          $this->assertFalse($result['assessingallowed']);
 273          $this->assertFalse($result['assessingexamplesallowed']);
 274          $this->assertTrue($result['examplesassessedbeforesubmission']);
 275          $this->assertTrue($result['examplesassessedbeforeassessment']);
 276  
 277          // Switch phase.
 278          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 279          $workshop->switch_phase(workshop::PHASE_SUBMISSION);
 280          $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
 281          $result = \external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
 282  
 283          $this->assertTrue($result['creatingsubmissionallowed']);
 284          $this->assertTrue($result['modifyingsubmissionallowed']);
 285          $this->assertFalse($result['assessingallowed']);
 286          $this->assertFalse($result['assessingexamplesallowed']);
 287          $this->assertTrue($result['examplesassessedbeforesubmission']);
 288          $this->assertTrue($result['examplesassessedbeforeassessment']);
 289  
 290          // Switch to next (to assessment).
 291          $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
 292          $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
 293          $result = \external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
 294  
 295          $this->assertFalse($result['creatingsubmissionallowed']);
 296          $this->assertFalse($result['modifyingsubmissionallowed']);
 297          $this->assertTrue($result['assessingallowed']);
 298          $this->assertFalse($result['assessingexamplesallowed']);
 299          $this->assertTrue($result['examplesassessedbeforesubmission']);
 300          $this->assertTrue($result['examplesassessedbeforeassessment']);
 301      }
 302  
 303      /**
 304       * Test mod_workshop_get_workshop_access_information for teachers.
 305       */
 306      public function test_mod_workshop_get_workshop_access_information_teacher() {
 307  
 308          self::setUser($this->teacher);
 309          $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
 310          $result = \external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
 311          // Check default values.
 312          $disabledcaps = array('canpeerassess', 'cansubmit');
 313  
 314          foreach ($result as $capname => $capvalue) {
 315              if (strpos($capname, 'can') !== 0) {
 316                  continue;
 317              }
 318              if (in_array($capname, $disabledcaps)) {
 319                  $this->assertFalse($capvalue);
 320              } else {
 321                  $this->assertTrue($capvalue);
 322              }
 323          }
 324  
 325          // Now, specific functionalities.
 326          $this->assertFalse($result['creatingsubmissionallowed']);
 327          $this->assertFalse($result['modifyingsubmissionallowed']);
 328          $this->assertFalse($result['assessingallowed']);
 329          $this->assertFalse($result['assessingexamplesallowed']);
 330      }
 331  
 332      /**
 333       * Test mod_workshop_get_user_plan for students.
 334       */
 335      public function test_mod_workshop_get_user_plan_student() {
 336  
 337          self::setUser($this->student);
 338          $result = mod_workshop_external::get_user_plan($this->workshop->id);
 339          $result = \external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
 340  
 341          $this->assertCount(0, $result['userplan']['examples']);  // No examples given.
 342          $this->assertCount(5, $result['userplan']['phases']);  // Always 5 phases.
 343          $this->assertEquals(workshop::PHASE_SETUP, $result['userplan']['phases'][0]['code']);  // First phase always setup.
 344          $this->assertTrue($result['userplan']['phases'][0]['active']); // First phase "Setup" active in new workshops.
 345  
 346          // Switch phase.
 347          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 348          $workshop->switch_phase(workshop::PHASE_SUBMISSION);
 349  
 350          $result = mod_workshop_external::get_user_plan($this->workshop->id);
 351          $result = \external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
 352  
 353          $this->assertEquals(workshop::PHASE_SUBMISSION, $result['userplan']['phases'][1]['code']);
 354          $this->assertTrue($result['userplan']['phases'][1]['active']); // We are now in submission phase.
 355      }
 356  
 357      /**
 358       * Test mod_workshop_get_user_plan for teachers.
 359       */
 360      public function test_mod_workshop_get_user_plan_teacher() {
 361  
 362          self::setUser($this->teacher);
 363          $result = mod_workshop_external::get_user_plan($this->workshop->id);
 364          $result = \external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
 365  
 366          $this->assertCount(0, $result['userplan']['examples']);  // No examples given.
 367          $this->assertCount(5, $result['userplan']['phases']);  // Always 5 phases.
 368          $this->assertEquals(workshop::PHASE_SETUP, $result['userplan']['phases'][0]['code']);  // First phase always setup.
 369          $this->assertTrue($result['userplan']['phases'][0]['active']); // First phase "Setup" active in new workshops.
 370          $this->assertCount(4, $result['userplan']['phases'][0]['tasks']);  // For new empty workshops, always 4 tasks.
 371  
 372          foreach ($result['userplan']['phases'][0]['tasks'] as $task) {
 373              if ($task['code'] == 'intro' || $task['code'] == 'instructauthors' || $task['code'] == 'editform') {
 374                  $this->assertEquals(1, $task['completed']);
 375              } else {
 376                  $this->assertEmpty($task['completed']);
 377              }
 378          }
 379  
 380          // Do some of the tasks asked - switch phase.
 381          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 382          $workshop->switch_phase(workshop::PHASE_SUBMISSION);
 383  
 384          $result = mod_workshop_external::get_user_plan($this->workshop->id);
 385          $result = \external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
 386          foreach ($result['userplan']['phases'][0]['tasks'] as $task) {
 387              if ($task['code'] == 'intro' || $task['code'] == 'instructauthors' || $task['code'] == 'editform' ||
 388                      $task['code'] == 'switchtonextphase') {
 389                  $this->assertEquals(1, $task['completed']);
 390              } else {
 391                  $this->assertEmpty($task['completed']);
 392              }
 393          }
 394  
 395          $result = mod_workshop_external::get_user_plan($this->workshop->id);
 396          $result = \external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
 397  
 398          $this->assertEquals(workshop::PHASE_SUBMISSION, $result['userplan']['phases'][1]['code']);
 399          $this->assertTrue($result['userplan']['phases'][1]['active']); // We are now in submission phase.
 400      }
 401  
 402      /**
 403       * Test test_view_workshop invalid id.
 404       */
 405      public function test_view_workshop_invalid_id() {
 406          $this->expectException('moodle_exception');
 407          mod_workshop_external::view_workshop(0);
 408      }
 409  
 410      /**
 411       * Test test_view_workshop user not enrolled.
 412       */
 413      public function test_view_workshop_user_not_enrolled() {
 414          // Test not-enrolled user.
 415          $usernotenrolled = self::getDataGenerator()->create_user();
 416          $this->setUser($usernotenrolled);
 417          $this->expectException('moodle_exception');
 418          mod_workshop_external::view_workshop($this->workshop->id);
 419      }
 420  
 421      /**
 422       * Test test_view_workshop user student.
 423       */
 424      public function test_view_workshop_user_student() {
 425          // Test user with full capabilities.
 426          $this->setUser($this->student);
 427  
 428          // Trigger and capture the event.
 429          $sink = $this->redirectEvents();
 430  
 431          $result = mod_workshop_external::view_workshop($this->workshop->id);
 432          $result = \external_api::clean_returnvalue(mod_workshop_external::view_workshop_returns(), $result);
 433          $this->assertTrue($result['status']);
 434  
 435          $events = $sink->get_events();
 436          $this->assertCount(1, $events);
 437          $event = array_shift($events);
 438  
 439          // Checking that the event contains the expected values.
 440          $this->assertInstanceOf('\mod_workshop\event\course_module_viewed', $event);
 441          $this->assertEquals($this->context, $event->get_context());
 442          $moodleworkshop = new \moodle_url('/mod/workshop/view.php', array('id' => $this->cm->id));
 443          $this->assertEquals($moodleworkshop, $event->get_url());
 444          $this->assertEventContextNotUsed($event);
 445          $this->assertNotEmpty($event->get_name());
 446      }
 447  
 448      /**
 449       * Test test_view_workshop user missing capabilities.
 450       */
 451      public function test_view_workshop_user_missing_capabilities() {
 452          // Test user with no capabilities.
 453          // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
 454          assign_capability('mod/workshop:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
 455          // Empty all the caches that may be affected  by this change.
 456          accesslib_clear_all_caches_for_unit_testing();
 457          \course_modinfo::clear_instance_cache();
 458  
 459          $this->setUser($this->student);
 460          $this->expectException('moodle_exception');
 461          mod_workshop_external::view_workshop($this->workshop->id);
 462      }
 463  
 464      /**
 465       * Test test_add_submission.
 466       */
 467      public function test_add_submission() {
 468          $fs = get_file_storage();
 469  
 470          // Test user with full capabilities.
 471          $this->setUser($this->student);
 472  
 473          $title = 'Submission title';
 474          $content = 'Submission contents';
 475  
 476          // Create a file in a draft area for inline attachments.
 477          $draftidinlineattach = file_get_unused_draft_itemid();
 478          $usercontext = \context_user::instance($this->student->id);
 479          $filenameimg = 'shouldbeanimage.txt';
 480          $filerecordinline = array(
 481              'contextid' => $usercontext->id,
 482              'component' => 'user',
 483              'filearea'  => 'draft',
 484              'itemid'    => $draftidinlineattach,
 485              'filepath'  => '/',
 486              'filename'  => $filenameimg,
 487          );
 488          $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
 489  
 490          // Create a file in a draft area for regular attachments.
 491          $draftidattach = file_get_unused_draft_itemid();
 492          $filerecordattach = $filerecordinline;
 493          $attachfilename = 'attachment.txt';
 494          $filerecordattach['filename'] = $attachfilename;
 495          $filerecordattach['itemid'] = $draftidattach;
 496          $fs->create_file_from_string($filerecordattach, 'simple text attachment');
 497  
 498          // Switch to submission phase.
 499          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 500          $workshop->switch_phase(workshop::PHASE_SUBMISSION);
 501  
 502          $result = mod_workshop_external::add_submission($this->workshop->id, $title, $content, FORMAT_MOODLE, $draftidinlineattach,
 503              $draftidattach);
 504          $result = \external_api::clean_returnvalue(mod_workshop_external::add_submission_returns(), $result);
 505          $this->assertEmpty($result['warnings']);
 506  
 507          // Check submission created.
 508          $submission = $workshop->get_submission_by_author($this->student->id);
 509          $this->assertTrue($result['status']);
 510          $this->assertEquals($result['submissionid'], $submission->id);
 511          $this->assertEquals($title, $submission->title);
 512          $this->assertEquals($content, $submission->content);
 513  
 514          // Check files.
 515          $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_content', $submission->id);
 516          $this->assertCount(2, $contentfiles);
 517          foreach ($contentfiles as $file) {
 518              if ($file->is_directory()) {
 519                  continue;
 520              } else {
 521                  $this->assertEquals($filenameimg, $file->get_filename());
 522              }
 523          }
 524          $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_attachment', $submission->id);
 525          $this->assertCount(2, $contentfiles);
 526          foreach ($contentfiles as $file) {
 527              if ($file->is_directory()) {
 528                  continue;
 529              } else {
 530                  $this->assertEquals($attachfilename, $file->get_filename());
 531              }
 532          }
 533      }
 534  
 535      /**
 536       * Test test_add_submission invalid phase.
 537       */
 538      public function test_add_submission_invalid_phase() {
 539          $this->setUser($this->student);
 540  
 541          $this->expectException('moodle_exception');
 542          mod_workshop_external::add_submission($this->workshop->id, 'Test');
 543      }
 544  
 545      /**
 546       * Test test_add_submission empty title.
 547       */
 548      public function test_add_submission_empty_title() {
 549          $this->setUser($this->student);
 550  
 551          // Switch to submission phase.
 552          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 553          $workshop->switch_phase(workshop::PHASE_SUBMISSION);
 554  
 555          $this->expectException('moodle_exception');
 556          mod_workshop_external::add_submission($this->workshop->id, '');
 557      }
 558  
 559      /**
 560       * Test test_add_submission already added.
 561       */
 562      public function test_add_submission_already_added() {
 563          $this->setUser($this->student);
 564  
 565          $usercontext = \context_user::instance($this->student->id);
 566          $fs = get_file_storage();
 567          $draftidattach = file_get_unused_draft_itemid();
 568          $filerecordattach = [
 569              'contextid' => $usercontext->id,
 570              'component' => 'user',
 571              'filearea'  => 'draft',
 572              'itemid'    => $draftidattach,
 573              'filepath'  => '/',
 574              'filename'  => 'attachement.txt'
 575          ];
 576          $fs->create_file_from_string($filerecordattach, 'simple text attachment');
 577  
 578          // Switch to submission phase.
 579          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 580          $workshop->switch_phase(workshop::PHASE_SUBMISSION);
 581  
 582          // Create the submission.
 583          $result = mod_workshop_external::add_submission($this->workshop->id, 'My submission', '', FORMAT_MOODLE, 0, $draftidattach);
 584          $result = \external_api::clean_returnvalue(mod_workshop_external::add_submission_returns(), $result);
 585  
 586          // Try to create it again.
 587          $result = mod_workshop_external::add_submission($this->workshop->id, 'My submission', '', FORMAT_MOODLE, 0, $draftidattach);
 588          $result = \external_api::clean_returnvalue(mod_workshop_external::add_submission_returns(), $result);
 589          $this->assertFalse($result['status']);
 590          $this->assertArrayNotHasKey('submissionid', $result);
 591          $this->assertCount(1, $result['warnings']);
 592          $this->assertEquals('fielderror', $result['warnings'][0]['warningcode']);
 593          $this->assertEquals('title', $result['warnings'][0]['item']);
 594      }
 595  
 596      /**
 597       * Helper method to create a submission for testing for the given user.
 598       *
 599       * @param int $user the submission will be created by this student.
 600       * @return int the submission id
 601       */
 602      protected function create_test_submission($user) {
 603          // Test user with full capabilities.
 604          $this->setUser($user);
 605  
 606          $title = 'Submission title';
 607          $content = 'Submission contents';
 608  
 609          // Create a file in a draft area for inline attachments.
 610          $fs = get_file_storage();
 611          $draftidinlineattach = file_get_unused_draft_itemid();
 612          $usercontext = \context_user::instance($user->id);
 613          $filenameimg = 'shouldbeanimage.txt';
 614          $filerecordinline = array(
 615              'contextid' => $usercontext->id,
 616              'component' => 'user',
 617              'filearea'  => 'draft',
 618              'itemid'    => $draftidinlineattach,
 619              'filepath'  => '/',
 620              'filename'  => $filenameimg,
 621          );
 622          $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
 623  
 624          // Create a file in a draft area for regular attachments.
 625          $draftidattach = file_get_unused_draft_itemid();
 626          $filerecordattach = $filerecordinline;
 627          $attachfilename = 'attachment.txt';
 628          $filerecordattach['filename'] = $attachfilename;
 629          $filerecordattach['itemid'] = $draftidattach;
 630          $fs->create_file_from_string($filerecordattach, 'simple text attachment');
 631  
 632          // Switch to submission phase.
 633          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 634          $workshop->switch_phase(workshop::PHASE_SUBMISSION);
 635  
 636          $result = mod_workshop_external::add_submission($this->workshop->id, $title, $content, FORMAT_MOODLE, $draftidinlineattach,
 637              $draftidattach);
 638          return $result['submissionid'];
 639      }
 640  
 641      /**
 642       * Test test_update_submission.
 643       */
 644      public function test_update_submission() {
 645  
 646          // Create the submission that will be updated.
 647          $submissionid = $this->create_test_submission($this->student);
 648  
 649          // Test user with full capabilities.
 650          $this->setUser($this->student);
 651  
 652          $title = 'Submission new title';
 653          $content = 'Submission new contents';
 654  
 655          // Create a different file in a draft area for inline attachments.
 656          $fs = get_file_storage();
 657          $draftidinlineattach = file_get_unused_draft_itemid();
 658          $usercontext = \context_user::instance($this->student->id);
 659          $filenameimg = 'shouldbeanimage_new.txt';
 660          $filerecordinline = array(
 661              'contextid' => $usercontext->id,
 662              'component' => 'user',
 663              'filearea'  => 'draft',
 664              'itemid'    => $draftidinlineattach,
 665              'filepath'  => '/',
 666              'filename'  => $filenameimg,
 667          );
 668          $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
 669  
 670          // Create a different file in a draft area for regular attachments.
 671          $draftidattach = file_get_unused_draft_itemid();
 672          $filerecordattach = $filerecordinline;
 673          $attachfilename = 'attachment_new.txt';
 674          $filerecordattach['filename'] = $attachfilename;
 675          $filerecordattach['itemid'] = $draftidattach;
 676          $fs->create_file_from_string($filerecordattach, 'simple text attachment');
 677  
 678          $result = mod_workshop_external::update_submission($submissionid, $title, $content, FORMAT_MOODLE, $draftidinlineattach,
 679              $draftidattach);
 680          $result = \external_api::clean_returnvalue(mod_workshop_external::update_submission_returns(), $result);
 681          $this->assertEmpty($result['warnings']);
 682  
 683          // Check submission updated.
 684          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 685          $submission = $workshop->get_submission_by_id($submissionid);
 686          $this->assertTrue($result['status']);
 687          $this->assertEquals($title, $submission->title);
 688          $this->assertEquals($content, $submission->content);
 689  
 690          // Check files.
 691          $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_content', $submission->id);
 692          $this->assertCount(2, $contentfiles);
 693          foreach ($contentfiles as $file) {
 694              if ($file->is_directory()) {
 695                  continue;
 696              } else {
 697                  $this->assertEquals($filenameimg, $file->get_filename());
 698              }
 699          }
 700          $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_attachment', $submission->id);
 701          $this->assertCount(2, $contentfiles);
 702          foreach ($contentfiles as $file) {
 703              if ($file->is_directory()) {
 704                  continue;
 705              } else {
 706                  $this->assertEquals($attachfilename, $file->get_filename());
 707              }
 708          }
 709      }
 710  
 711      /**
 712       * Test test_update_submission belonging to other user.
 713       */
 714      public function test_update_submission_of_other_user() {
 715          // Create the submission that will be updated.
 716          $submissionid = $this->create_test_submission($this->student);
 717  
 718          $this->setUser($this->teacher);
 719  
 720          $this->expectException('moodle_exception');
 721          mod_workshop_external::update_submission($submissionid, 'Test');
 722      }
 723  
 724      /**
 725       * Test test_update_submission invalid phase.
 726       */
 727      public function test_update_submission_invalid_phase() {
 728          // Create the submission that will be updated.
 729          $submissionid = $this->create_test_submission($this->student);
 730  
 731          $this->setUser($this->student);
 732  
 733          // Switch to assessment phase.
 734          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 735          $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
 736  
 737          $this->expectException('moodle_exception');
 738          mod_workshop_external::update_submission($submissionid, 'Test');
 739      }
 740  
 741      /**
 742       * Test test_update_submission empty title.
 743       */
 744      public function test_update_submission_empty_title() {
 745          // Create the submission that will be updated.
 746          $submissionid = $this->create_test_submission($this->student);
 747  
 748          $this->setUser($this->student);
 749  
 750          $this->expectException('moodle_exception');
 751          mod_workshop_external::update_submission($submissionid, '');
 752      }
 753  
 754      /**
 755       * Test test_delete_submission.
 756       */
 757      public function test_delete_submission() {
 758  
 759          // Create the submission that will be deleted.
 760          $submissionid = $this->create_test_submission($this->student);
 761  
 762          $this->setUser($this->student);
 763  
 764          // Trigger and capture the event.
 765          $sink = $this->redirectEvents();
 766  
 767          $result = mod_workshop_external::delete_submission($submissionid);
 768          $result = \external_api::clean_returnvalue(mod_workshop_external::delete_submission_returns(), $result);
 769          $this->assertEmpty($result['warnings']);
 770          $this->assertTrue($result['status']);
 771          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 772          $submission = $workshop->get_submission_by_author($this->student->id);
 773          $this->assertFalse($submission);
 774  
 775          $events = $sink->get_events();
 776          $this->assertCount(1, $events);
 777          $event = array_shift($events);
 778  
 779          // Checking event.
 780          $this->assertInstanceOf('\mod_workshop\event\submission_deleted', $event);
 781          $this->assertEquals($this->context, $event->get_context());
 782      }
 783  
 784      /**
 785       * Test test_delete_submission_with_assessments.
 786       */
 787      public function test_delete_submission_with_assessments() {
 788  
 789          // Create the submission that will be deleted.
 790          $submissionid = $this->create_test_submission($this->student);
 791  
 792          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
 793          $workshopgenerator->create_assessment($submissionid, $this->teacher->id, array(
 794              'weight' => 3,
 795              'grade' => 95.00000,
 796          ));
 797  
 798          $this->setUser($this->student);
 799          $this->expectException('moodle_exception');
 800          mod_workshop_external::delete_submission($submissionid);
 801      }
 802  
 803      /**
 804       * Test test_delete_submission_invalid_phase.
 805       */
 806      public function test_delete_submission_invalid_phase() {
 807  
 808          // Create the submission that will be deleted.
 809          $submissionid = $this->create_test_submission($this->student);
 810  
 811          // Switch to assessment phase.
 812          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 813          $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
 814  
 815          $this->setUser($this->student);
 816          $this->expectException('moodle_exception');
 817          mod_workshop_external::delete_submission($submissionid);
 818      }
 819  
 820      /**
 821       * Test test_delete_submission_as_teacher.
 822       */
 823      public function test_delete_submission_as_teacher() {
 824  
 825          // Create the submission that will be deleted.
 826          $submissionid = $this->create_test_submission($this->student);
 827  
 828          $this->setUser($this->teacher);
 829          $result = mod_workshop_external::delete_submission($submissionid);
 830          $result = \external_api::clean_returnvalue(mod_workshop_external::delete_submission_returns(), $result);
 831          $this->assertEmpty($result['warnings']);
 832          $this->assertTrue($result['status']);
 833      }
 834  
 835      /**
 836       * Test test_delete_submission_other_user.
 837       */
 838      public function test_delete_submission_other_user() {
 839  
 840          $anotheruser = self::getDataGenerator()->create_user();
 841          $this->getDataGenerator()->enrol_user($anotheruser->id, $this->course->id, $this->studentrole->id, 'manual');
 842          // Create the submission that will be deleted.
 843          $submissionid = $this->create_test_submission($this->student);
 844  
 845          $this->setUser($anotheruser);
 846          $this->expectException('moodle_exception');
 847          mod_workshop_external::delete_submission($submissionid);
 848      }
 849  
 850      /**
 851       * Test test_get_submissions_student.
 852       */
 853      public function test_get_submissions_student() {
 854  
 855          // Create a couple of submissions with files.
 856          $firstsubmissionid = $this->create_test_submission($this->student);  // Create submission with files.
 857          $secondsubmissionid = $this->create_test_submission($this->anotherstudentg1);
 858  
 859          $this->setUser($this->student);
 860          $result = mod_workshop_external::get_submissions($this->workshop->id);
 861          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
 862          // We should get just our submission.
 863          $this->assertCount(1, $result['submissions']);
 864          $this->assertEquals(1, $result['totalcount']);
 865          $this->assertEquals($firstsubmissionid, $result['submissions'][0]['id']);
 866          $this->assertCount(1, $result['submissions'][0]['contentfiles']); // Check we retrieve submission text files.
 867          $this->assertCount(1, $result['submissions'][0]['attachmentfiles']); // Check we retrieve attachment files.
 868          // We shoul not see the grade or feedback information.
 869          $properties = submission_exporter::properties_definition();
 870          foreach ($properties as $attribute => $settings) {
 871              if (!empty($settings['optional'])) {
 872                  if (isset($result['submissions'][0][$attribute])) {
 873                      echo "error $attribute";
 874                  }
 875                  $this->assertFalse(isset($result['submissions'][0][$attribute]));
 876              }
 877          }
 878      }
 879  
 880      /**
 881       * Test test_get_submissions_published_student.
 882       */
 883      public function test_get_submissions_published_student() {
 884  
 885          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 886          $workshop->switch_phase(workshop::PHASE_CLOSED);
 887          // Create a couple of submissions with files.
 888          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
 889          $submission = array('published' => 1);
 890          $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id, $submission);
 891  
 892          $this->setUser($this->student);
 893          $result = mod_workshop_external::get_submissions($this->workshop->id);
 894          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
 895          // We should get just our submission.
 896          $this->assertCount(1, $result['submissions']);
 897          $this->assertEquals(1, $result['totalcount']);
 898          $this->assertEquals($submissionid, $result['submissions'][0]['id']);
 899  
 900          // Check with group restrictions.
 901          $this->setUser($this->anotherstudentg2);
 902          $result = mod_workshop_external::get_submissions($this->workshop->id);
 903          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
 904          $this->assertCount(0, $result['submissions']);  // I can't see other users in separated groups.
 905          $this->assertEquals(0, $result['totalcount']);
 906      }
 907  
 908      /**
 909       * Test test_get_submissions_from_student_with_feedback_from_teacher.
 910       */
 911      public function test_get_submissions_from_student_with_feedback_from_teacher() {
 912          global $DB;
 913  
 914          // Create a couple of submissions with files.
 915          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
 916          $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
 917          // Create teacher feedback for submission.
 918          $record = new \stdClass();
 919          $record->id = $submissionid;
 920          $record->gradeover = 9;
 921          $record->gradeoverby = $this->teacher->id;
 922          $record->feedbackauthor = 'Hey';
 923          $record->feedbackauthorformat = FORMAT_MOODLE;
 924          $record->published = 1;
 925          $DB->update_record('workshop_submissions', $record);
 926  
 927          // Remove teacher caps.
 928          assign_capability('mod/workshop:viewallsubmissions', CAP_PROHIBIT, $this->teacher->id, $this->context->id);
 929          // Empty all the caches that may be affected  by this change.
 930          accesslib_clear_all_caches_for_unit_testing();
 931          \course_modinfo::clear_instance_cache();
 932  
 933          $this->setUser($this->teacher);
 934          $result = mod_workshop_external::get_submissions($this->workshop->id, $this->student->id);
 935          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
 936          // We should get just our submission.
 937          $this->assertEquals(1, $result['totalcount']);
 938          $this->assertEquals($submissionid, $result['submissions'][0]['id']);
 939      }
 940  
 941      /**
 942       * Test test_get_submissions_from_students_as_teacher.
 943       */
 944      public function test_get_submissions_from_students_as_teacher() {
 945  
 946          // Create a couple of submissions with files.
 947          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
 948          $submissionid1 = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
 949          $submissionid2 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id);
 950          $submissionid3 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg2->id);
 951  
 952          $this->setUser($this->teacher);
 953          $result = mod_workshop_external::get_submissions($this->workshop->id); // Get all.
 954          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
 955          $this->assertEquals(3, $result['totalcount']);
 956          $this->assertCount(3, $result['submissions']);
 957  
 958          $result = mod_workshop_external::get_submissions($this->workshop->id, 0, 0, 0, 2); // Check pagination.
 959          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
 960          $this->assertEquals(3, $result['totalcount']);
 961          $this->assertCount(2, $result['submissions']);
 962  
 963          $result = mod_workshop_external::get_submissions($this->workshop->id, 0, $this->group2->id); // Get group 2.
 964          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
 965          $this->assertEquals(1, $result['totalcount']);
 966          $this->assertCount(1, $result['submissions']);
 967          $this->assertEquals($submissionid3, $result['submissions'][0]['id']);
 968  
 969          $result = mod_workshop_external::get_submissions($this->workshop->id, $this->anotherstudentg1->id); // Get one.
 970          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
 971          $this->assertEquals(1, $result['totalcount']);
 972          $this->assertEquals($submissionid2, $result['submissions'][0]['id']);
 973      }
 974  
 975      /**
 976       * Test test_get_submission_student.
 977       */
 978      public function test_get_submission_student() {
 979  
 980          // Create a couple of submissions with files.
 981          $firstsubmissionid = $this->create_test_submission($this->student);  // Create submission with files.
 982  
 983          $workshop = new workshop($this->workshop, $this->cm, $this->course);
 984          $workshop->switch_phase(workshop::PHASE_CLOSED);
 985          $this->setUser($this->student);
 986          $result = mod_workshop_external::get_submission($firstsubmissionid);
 987          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
 988          $this->assertEquals($firstsubmissionid, $result['submission']['id']);
 989          $this->assertCount(1, $result['submission']['contentfiles']); // Check we retrieve submission text files.
 990          $this->assertCount(1, $result['submission']['attachmentfiles']); // Check we retrieve attachment files.
 991          $this->assertArrayHasKey('feedbackauthor', $result['submission']);
 992          $this->assertArrayNotHasKey('grade', $result['submission']);
 993          $this->assertArrayNotHasKey('gradeover', $result['submission']);
 994          $this->assertArrayHasKey('gradeoverby', $result['submission']);
 995          $this->assertArrayNotHasKey('timegraded', $result['submission']);
 996  
 997          // Switch to a different phase (where feedback won't be available).
 998          $workshop->switch_phase(workshop::PHASE_EVALUATION);
 999          $result = mod_workshop_external::get_submission($firstsubmissionid);
1000          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1001          $this->assertEquals($firstsubmissionid, $result['submission']['id']);
1002          $this->assertCount(1, $result['submission']['contentfiles']); // Check we retrieve submission text files.
1003          $this->assertCount(1, $result['submission']['attachmentfiles']); // Check we retrieve attachment files.
1004          $this->assertArrayNotHasKey('feedbackauthor', $result['submission']);
1005          $this->assertArrayNotHasKey('grade', $result['submission']);
1006          $this->assertArrayNotHasKey('gradeover', $result['submission']);
1007          $this->assertArrayNotHasKey('gradeoverby', $result['submission']);
1008          $this->assertArrayNotHasKey('timegraded', $result['submission']);
1009      }
1010  
1011      /**
1012       * Test test_get_submission_i_reviewed.
1013       */
1014      public function test_get_submission_i_reviewed() {
1015  
1016          // Create a couple of submissions with files.
1017          $firstsubmissionid = $this->create_test_submission($this->student);  // Create submission with files.
1018          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1019          $workshopgenerator->create_assessment($firstsubmissionid, $this->anotherstudentg1->id, array(
1020              'weight' => 3,
1021              'grade' => 95,
1022          ));
1023          // Now try to get the submission I just reviewed.
1024          $this->setUser($this->anotherstudentg1);
1025          $result = mod_workshop_external::get_submission($firstsubmissionid);
1026          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1027          $this->assertEquals($firstsubmissionid, $result['submission']['id']);
1028          $this->assertCount(1, $result['submission']['contentfiles']); // Check we retrieve submission text files.
1029          $this->assertCount(1, $result['submission']['attachmentfiles']); // Check we retrieve attachment files.
1030          $this->assertArrayNotHasKey('feedbackauthor', $result['submission']);
1031          $this->assertArrayNotHasKey('grade', $result['submission']);
1032          $this->assertArrayNotHasKey('gradeover', $result['submission']);
1033          $this->assertArrayNotHasKey('gradeoverby', $result['submission']);
1034          $this->assertArrayNotHasKey('timegraded', $result['submission']);
1035      }
1036  
1037      /**
1038       * Test test_get_submission_other_student.
1039       */
1040      public function test_get_submission_other_student() {
1041  
1042          // Create a couple of submissions with files.
1043          $firstsubmissionid = $this->create_test_submission($this->student);  // Create submission with files.
1044          // Expect failure.
1045          $this->setUser($this->anotherstudentg1);
1046          $this->expectException('moodle_exception');
1047          $result = mod_workshop_external::get_submission($firstsubmissionid);
1048      }
1049  
1050      /**
1051       * Test test_get_submission_published_student.
1052       */
1053      public function test_get_submission_published_student() {
1054  
1055          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1056          $workshop->switch_phase(workshop::PHASE_CLOSED);
1057          // Create a couple of submissions with files.
1058          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1059          $submission = array('published' => 1);
1060          $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id, $submission);
1061  
1062          $this->setUser($this->student);
1063          $result = mod_workshop_external::get_submission($submissionid);
1064          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1065          $this->assertEquals($submissionid, $result['submission']['id']);
1066          // Check that the student don't see the other student grade/feedback data even if is published.
1067          // We should not see the grade or feedback information.
1068          $properties = submission_exporter::properties_definition();
1069          $this->assertArrayNotHasKey('feedbackauthor', $result['submission']);
1070          $this->assertArrayNotHasKey('grade', $result['submission']);
1071          $this->assertArrayNotHasKey('gradeover', $result['submission']);
1072          $this->assertArrayNotHasKey('gradeoverby', $result['submission']);
1073          $this->assertArrayNotHasKey('timegraded', $result['submission']);
1074  
1075          // Check with group restrictions.
1076          $this->setUser($this->anotherstudentg2);
1077          $this->expectException('moodle_exception');
1078          mod_workshop_external::get_submission($submissionid);
1079      }
1080  
1081      /**
1082       * Test test_get_submission_from_student_with_feedback_from_teacher.
1083       */
1084      public function test_get_submission_from_student_with_feedback_from_teacher() {
1085          global $DB;
1086  
1087          // Create a couple of submissions with files.
1088          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1089          $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1090          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1091          $workshop->switch_phase(workshop::PHASE_CLOSED);
1092          // Create teacher feedback for submission.
1093          $record = new \stdClass();
1094          $record->id = $submissionid;
1095          $record->gradeover = 9;
1096          $record->gradeoverby = $this->teacher->id;
1097          $record->feedbackauthor = 'Hey';
1098          $record->feedbackauthorformat = FORMAT_MOODLE;
1099          $record->published = 1;
1100          $record->timegraded = time();
1101          $DB->update_record('workshop_submissions', $record);
1102  
1103          $this->setUser($this->teacher);
1104          $result = mod_workshop_external::get_submission($submissionid);
1105          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1106          $this->assertEquals($submissionid, $result['submission']['id']);
1107          $this->assertEquals($record->feedbackauthor, $result['submission']['feedbackauthor']);
1108          $this->assertEquals($record->gradeover, $result['submission']['gradeover']);
1109          $this->assertEquals($record->gradeoverby, $result['submission']['gradeoverby']);
1110          $this->assertEquals($record->timegraded, $result['submission']['timegraded']);
1111  
1112          // Go to phase where feedback and grades are not yet available.
1113          $workshop->switch_phase(workshop::PHASE_SUBMISSION);
1114          $result = mod_workshop_external::get_submission($submissionid);
1115          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1116          $this->assertArrayNotHasKey('feedbackauthor', $result['submission']);
1117          $this->assertArrayNotHasKey('grade', $result['submission']);
1118          $this->assertArrayNotHasKey('gradeover', $result['submission']);
1119          $this->assertArrayNotHasKey('gradeoverby', $result['submission']);
1120          $this->assertArrayNotHasKey('timegraded', $result['submission']);
1121  
1122          // Remove teacher caps to view and go to valid phase.
1123          $workshop->switch_phase(workshop::PHASE_EVALUATION);
1124          unassign_capability('mod/workshop:viewallsubmissions', $this->teacherrole->id);
1125          // Empty all the caches that may be affected  by this change.
1126          accesslib_clear_all_caches_for_unit_testing();
1127  
1128          $this->expectException('moodle_exception');
1129          mod_workshop_external::get_submission($submissionid);
1130      }
1131  
1132      /**
1133       * Test test_get_submission_from_students_as_teacher.
1134       */
1135      public function test_get_submission_from_students_as_teacher() {
1136          // Create a couple of submissions with files.
1137          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1138          $submissionid1 = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1139          $submissionid2 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id);
1140          $submissionid3 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg2->id);
1141  
1142          $this->setUser($this->teacher);
1143          $result = mod_workshop_external::get_submission($submissionid1); // Get all.
1144          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1145          $this->assertEquals($submissionid1, $result['submission']['id']);
1146  
1147          $result = mod_workshop_external::get_submission($submissionid3); // Get group 2.
1148          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1149          $this->assertEquals($submissionid3, $result['submission']['id']);
1150      }
1151  
1152  
1153      /**
1154       * Test get_submission_assessments_student.
1155       */
1156      public function test_get_submission_assessments_student() {
1157  
1158          // Create the submission that will be deleted.
1159          $submissionid = $this->create_test_submission($this->student);
1160  
1161          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1162          $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1163              'weight' => 3,
1164              'grade' => 95,
1165          ));
1166          $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1167              'weight' => 2,
1168              'grade' => 90,
1169          ));
1170  
1171          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1172          $workshop->switch_phase(workshop::PHASE_CLOSED);
1173          $this->setUser($this->student);
1174          $result = mod_workshop_external::get_submission_assessments($submissionid);
1175          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submission_assessments_returns(), $result);
1176          $this->assertCount(2, $result['assessments']);  // I received my two assessments.
1177          foreach ($result['assessments'] as $assessment) {
1178              if ($assessment['grade'] == 90) {
1179                  // My own assessment, I can see me.
1180                  $this->assertEquals($this->student->id, $assessment['reviewerid']);
1181              } else {
1182                  // Student's can't see who did the review.
1183                  $this->assertEquals(0, $assessment['reviewerid']);
1184              }
1185          }
1186      }
1187  
1188      /**
1189       * Test get_submission_assessments_invalid_phase.
1190       */
1191      public function test_get_submission_assessments_invalid_phase() {
1192  
1193          // Create the submission that will be deleted.
1194          $submissionid = $this->create_test_submission($this->student);
1195  
1196          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1197          $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1198              'weight' => 3,
1199              'grade' => 95,
1200          ));
1201  
1202          $this->expectException('moodle_exception');
1203          mod_workshop_external::get_submission_assessments($submissionid);
1204      }
1205  
1206      /**
1207       * Test get_submission_assessments_teacher.
1208       */
1209      public function test_get_submission_assessments_teacher() {
1210  
1211          // Create the submission that will be deleted.
1212          $submissionid = $this->create_test_submission($this->student);
1213  
1214          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1215          $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1216              'weight' => 1,
1217              'grade' => 50,
1218          ));
1219  
1220          $this->setUser($this->teacher);
1221          $result = mod_workshop_external::get_submission_assessments($submissionid);
1222          $result = \external_api::clean_returnvalue(mod_workshop_external::get_submission_assessments_returns(), $result);
1223          $this->assertCount(1, $result['assessments']);
1224          $this->assertEquals(50, $result['assessments'][0]['grade']);
1225          $this->assertEquals($assessmentid, $result['assessments'][0]['id']);
1226      }
1227  
1228      /**
1229       * Test get_assessment_author.
1230       */
1231      public function test_get_assessment_author() {
1232  
1233          // Create the submission.
1234          $submissionid = $this->create_test_submission($this->anotherstudentg1);
1235  
1236          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1237          $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1238              'weight' => 2,
1239              'grade' => 90,
1240          ));
1241  
1242          // Switch to closed phase.
1243          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1244          $workshop->switch_phase(workshop::PHASE_CLOSED);
1245          $this->setUser($this->anotherstudentg1);
1246          $result = mod_workshop_external::get_assessment($assessmentid);
1247          $result = \external_api::clean_returnvalue(mod_workshop_external::get_assessment_returns(), $result);
1248          $this->assertEquals($assessmentid, $result['assessment']['id']);
1249          $this->assertEquals(90, $result['assessment']['grade']);
1250          // I can't see the reviewer review.
1251          $this->assertFalse(isset($result['assessment']['feedbackreviewer']));
1252      }
1253  
1254      /**
1255       * Test get_assessment_reviewer.
1256       */
1257      public function test_get_assessment_reviewer() {
1258  
1259          // Create the submission.
1260          $submissionid = $this->create_test_submission($this->anotherstudentg1);
1261  
1262          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1263          $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1264              'weight' => 2,
1265              'grade' => 90,
1266          ));
1267  
1268          // Switch to closed phase.
1269          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1270          $workshop->switch_phase(workshop::PHASE_CLOSED);
1271          $this->setUser($this->student);
1272          $result = mod_workshop_external::get_assessment($assessmentid);
1273          $result = \external_api::clean_returnvalue(mod_workshop_external::get_assessment_returns(), $result);
1274          $this->assertEquals($assessmentid, $result['assessment']['id']);
1275          $this->assertEquals(90, $result['assessment']['grade']);
1276          // I can see the reviewer review.
1277          $this->assertTrue(isset($result['assessment']['feedbackreviewer']));
1278      }
1279  
1280      /**
1281       * Test get_assessment_teacher.
1282       */
1283      public function test_get_assessment_teacher() {
1284  
1285          // Create the submission.
1286          $submissionid = $this->create_test_submission($this->anotherstudentg1);
1287  
1288          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1289          $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1290              'weight' => 2,
1291              'grade' => 90,
1292          ));
1293  
1294          // Switch to closed phase.
1295          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1296          $workshop->switch_phase(workshop::PHASE_CLOSED);
1297          $this->setUser($this->teacher);
1298          $result = mod_workshop_external::get_assessment($assessmentid);
1299          $result = \external_api::clean_returnvalue(mod_workshop_external::get_assessment_returns(), $result);
1300          $this->assertEquals($assessmentid, $result['assessment']['id']);
1301          $this->assertEquals(90, $result['assessment']['grade']);
1302      }
1303  
1304      /**
1305       * Test get_assessment_student_invalid_phase.
1306       */
1307      public function test_get_assessment_student_invalid_phase() {
1308  
1309          // Create the submission.
1310          $submissionid = $this->create_test_submission($this->anotherstudentg1);
1311  
1312          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1313          $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1314              'weight' => 2,
1315              'grade' => 90,
1316          ));
1317  
1318          // Switch to closed phase.
1319          $this->setUser($this->anotherstudentg1);
1320  
1321          $this->expectException('moodle_exception');
1322          mod_workshop_external::get_assessment($assessmentid);
1323      }
1324  
1325      /**
1326       * Test get_assessment_student_invalid_user.
1327       */
1328      public function test_get_assessment_student_invalid_user() {
1329  
1330          // Create the submission.
1331          $submissionid = $this->create_test_submission($this->anotherstudentg1);
1332  
1333          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1334          $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1335              'weight' => 2,
1336              'grade' => 90,
1337          ));
1338  
1339          // Switch to closed phase.
1340          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1341          $workshop->switch_phase(workshop::PHASE_CLOSED);
1342          $this->setUser($this->anotherstudentg2);
1343  
1344          $this->expectException('moodle_exception');
1345          mod_workshop_external::get_assessment($assessmentid);
1346      }
1347  
1348      /**
1349       * Test get_assessment_form_definition_reviewer_new_assessment.
1350       */
1351      public function test_get_assessment_form_definition_reviewer_new_assessment() {
1352  
1353          // Create the submission.
1354          $submissionid = $this->create_test_submission($this->anotherstudentg1);
1355  
1356          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1357          $submission = $workshop->get_submission_by_id($submissionid);
1358          $assessmentid = $workshop->add_allocation($submission, $this->student->id);
1359  
1360          // Switch to assessment phase.
1361          $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
1362          $this->setUser($this->student);
1363          $result = mod_workshop_external::get_assessment_form_definition($assessmentid);
1364          $result = \external_api::clean_returnvalue(mod_workshop_external::get_assessment_form_definition_returns(), $result);
1365          $this->assertEquals(4, $result['dimenssionscount']);    // We receive the expected 4 dimensions.
1366          $this->assertEmpty($result['current']); // Assessment not yet done.
1367          foreach ($result['fields'] as $field) {
1368              if (strpos($field['name'], 'grade__idx_') === 0) {
1369                  $this->assertEquals(25, $field['value']); // Check one of the dimension fields attributes.
1370              }
1371          }
1372          // Check dimensions grading info.
1373          foreach ($result['dimensionsinfo'] as $dimension) {
1374              $this->assertEquals(0, $dimension['min']);
1375              $this->assertEquals(25, $dimension['max']);
1376              $this->assertEquals(25, $dimension['weight']);
1377              $this->assertFalse(isset($dimension['scale']));
1378          }
1379      }
1380  
1381      /**
1382       * Test get_assessment_form_definition_teacher_new_assessment.
1383       */
1384      public function test_get_assessment_form_definition_teacher_new_assessment() {
1385  
1386          // Create the submission.
1387          $submissionid = $this->create_test_submission($this->anotherstudentg1);
1388  
1389          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1390          $submission = $workshop->get_submission_by_id($submissionid);
1391          $assessmentid = $workshop->add_allocation($submission, $this->student->id);
1392  
1393          // Switch to assessment phase.
1394          $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
1395          // Teachers need to be able to view assessments.
1396          $this->setUser($this->teacher);
1397          $result = mod_workshop_external::get_assessment_form_definition($assessmentid);
1398          $result = \external_api::clean_returnvalue(mod_workshop_external::get_assessment_form_definition_returns(), $result);
1399          $this->assertEquals(4, $result['dimenssionscount']);
1400      }
1401  
1402      /**
1403       * Test get_assessment_form_definition_invalid_phase.
1404       */
1405      public function test_get_assessment_form_definition_invalid_phase() {
1406  
1407          // Create the submission.
1408          $submissionid = $this->create_test_submission($this->anotherstudentg1);
1409  
1410          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1411          $submission = $workshop->get_submission_by_id($submissionid);
1412          $assessmentid = $workshop->add_allocation($submission, $this->anotherstudentg1->id);
1413  
1414          $workshop->switch_phase(workshop::PHASE_EVALUATION);
1415          $this->setUser($this->student);
1416          // Since we are not reviewers we can't see the assessment until the workshop is closed.
1417          $this->expectException('moodle_exception');
1418          mod_workshop_external::get_assessment_form_definition($assessmentid);
1419      }
1420  
1421      /**
1422       * Test get_reviewer_assessments.
1423       */
1424      public function test_get_reviewer_assessments() {
1425  
1426          // Create the submission.
1427          $submissionid1 = $this->create_test_submission($this->student);
1428          $submissionid2 = $this->create_test_submission($this->anotherstudentg1);
1429  
1430          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1431          $assessmentid1 = $workshopgenerator->create_assessment($submissionid1, $this->student->id, array(
1432              'weight' => 2,
1433              'grade' => 90,
1434          ));
1435          $assessmentid2 = $workshopgenerator->create_assessment($submissionid2, $this->student->id, array(
1436              'weight' => 3,
1437              'grade' => 80,
1438          ));
1439  
1440          // Switch to assessment phase.
1441          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1442          $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
1443          $this->setUser($this->student);
1444          // Get my assessments.
1445          $result = mod_workshop_external::get_reviewer_assessments($this->workshop->id);
1446          $result = \external_api::clean_returnvalue(mod_workshop_external::get_reviewer_assessments_returns(), $result);
1447          $this->assertCount(2, $result['assessments']);
1448          foreach ($result['assessments'] as $assessment) {
1449              if ($assessment['id'] == $assessmentid1) {
1450                  $this->assertEquals(90, $assessment['grade']);
1451              } else {
1452                  $this->assertEquals($assessmentid2, $assessment['id']);
1453                  $this->assertEquals(80, $assessment['grade']);
1454              }
1455          }
1456  
1457          // Now, as teacher try to get the same student assessments.
1458          $result = mod_workshop_external::get_reviewer_assessments($this->workshop->id, $this->student->id);
1459          $result = \external_api::clean_returnvalue(mod_workshop_external::get_reviewer_assessments_returns(), $result);
1460          $this->assertCount(2, $result['assessments']);
1461          $this->assertArrayNotHasKey('feedbackreviewer', $result['assessments'][0]);
1462      }
1463  
1464      /**
1465       * Test get_reviewer_assessments_other_student.
1466       */
1467      public function test_get_reviewer_assessments_other_student() {
1468  
1469          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1470          $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
1471          // Try to get other user assessments.
1472          $this->setUser($this->student);
1473          $this->expectException('moodle_exception');
1474          mod_workshop_external::get_reviewer_assessments($this->workshop->id, $this->anotherstudentg1->id);
1475      }
1476  
1477      /**
1478       * Test get_reviewer_assessments_invalid_phase.
1479       */
1480      public function test_get_reviewer_assessments_invalid_phase() {
1481  
1482          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1483          $workshop->switch_phase(workshop::PHASE_SUBMISSION);
1484          // Try to get other user assessments.
1485          $this->setUser($this->student);
1486          $this->expectException('moodle_exception');
1487          mod_workshop_external::get_reviewer_assessments($this->workshop->id, $this->anotherstudentg1->id);
1488      }
1489  
1490      /**
1491       * Test update_assessment.
1492       */
1493      public function test_update_assessment() {
1494  
1495          // Create the submission.
1496          $submissionid = $this->create_test_submission($this->anotherstudentg1);
1497  
1498          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1499          $submission = $workshop->get_submission_by_id($submissionid);
1500          $assessmentid = $workshop->add_allocation($submission, $this->student->id);
1501  
1502          // Switch to assessment phase.
1503          $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
1504          $this->setUser($this->student);
1505          // Get the form definition.
1506          $result = mod_workshop_external::get_assessment_form_definition($assessmentid);
1507          $result = \external_api::clean_returnvalue(mod_workshop_external::get_assessment_form_definition_returns(), $result);
1508  
1509          // Prepare the data to be sent.
1510          $data = $result['fields'];
1511          foreach ($data as $key => $param) {
1512              if (strpos($param['name'], 'peercomment__idx_') === 0) {
1513                  $data[$key]['value'] = 'Some content';
1514              } else if (strpos($param['name'], 'grade__idx_') === 0) {
1515                  $data[$key]['value'] = 25; // Set all to 25.
1516              }
1517          }
1518  
1519          // Required data.
1520          $data[] = array(
1521              'name' => 'nodims',
1522              'value' => $result['dimenssionscount'],
1523          );
1524  
1525          // General feedback.
1526          $data[] = array(
1527              'name' => 'feedbackauthor',
1528              'value' => 'Feedback for the author',
1529          );
1530          $data[] = array(
1531              'name' => 'feedbackauthorformat',
1532              'value' => FORMAT_MOODLE,
1533          );
1534  
1535          // Create a file in a draft area for inline attachments.
1536          $fs = get_file_storage();
1537          $draftidinlineattach = file_get_unused_draft_itemid();
1538          $usercontext = \context_user::instance($this->student->id);
1539          $filenameimg = 'shouldbeanimage.txt';
1540          $filerecordinline = array(
1541              'contextid' => $usercontext->id,
1542              'component' => 'user',
1543              'filearea'  => 'draft',
1544              'itemid'    => $draftidinlineattach,
1545              'filepath'  => '/',
1546              'filename'  => $filenameimg,
1547          );
1548          $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
1549  
1550          // Create a file in a draft area for regular attachments.
1551          $draftidattach = file_get_unused_draft_itemid();
1552          $filerecordattach = $filerecordinline;
1553          $attachfilename = 'attachment.txt';
1554          $filerecordattach['filename'] = $attachfilename;
1555          $filerecordattach['itemid'] = $draftidattach;
1556          $fs->create_file_from_string($filerecordattach, 'simple text attachment');
1557  
1558          $data[] = array(
1559              'name' => 'feedbackauthorinlineattachmentsid',
1560              'value' => $draftidinlineattach,
1561          );
1562          $data[] = array(
1563              'name' => 'feedbackauthorattachmentsid',
1564              'value' => $draftidattach,
1565          );
1566  
1567          // Update the assessment.
1568          $result = mod_workshop_external::update_assessment($assessmentid, $data);
1569          $result = \external_api::clean_returnvalue(mod_workshop_external::update_assessment_returns(), $result);
1570          $this->assertEquals(100, $result['rawgrade']);
1571          $this->assertTrue($result['status']);
1572  
1573          // Get the assessment and check it was updated properly.
1574          $result = mod_workshop_external::get_assessment($assessmentid);
1575          $result = \external_api::clean_returnvalue(mod_workshop_external::get_assessment_returns(), $result);
1576          $this->assertEquals(100, $result['assessment']['grade']);
1577          $this->assertEquals($this->student->id, $result['assessment']['reviewerid']);
1578          $this->assertEquals('Feedback for the author', $result['assessment']['feedbackauthor']);
1579          $this->assertCount(1, $result['assessment']['feedbackcontentfiles']);
1580          $this->assertCount(1, $result['assessment']['feedbackattachmentfiles']);
1581  
1582          // Now, get again the form and check we received the data we already sent.
1583          $result = mod_workshop_external::get_assessment_form_definition($assessmentid);
1584          $result = \external_api::clean_returnvalue(mod_workshop_external::get_assessment_form_definition_returns(), $result);
1585          foreach ($result['current'] as $currentdata) {
1586              if (strpos($currentdata['name'], 'peercomment__idx_') === 0) {
1587                  $this->assertEquals('Some content', $currentdata['value']);
1588              } else if (strpos($currentdata['name'], 'grade__idx_') === 0) {
1589                  $this->assertEquals(25, (int) $currentdata['value']);
1590              }
1591          }
1592      }
1593  
1594      /**
1595       * Test get_grades.
1596       */
1597      public function test_get_grades() {
1598  
1599          $timenow = time();
1600          $submissiongrade = array(
1601              'userid' => $this->student->id,
1602              'rawgrade' => 40,
1603              'feedback' => '',
1604              'feedbackformat' => 1,
1605              'datesubmitted' => $timenow,
1606              'dategraded' => $timenow,
1607          );
1608          $assessmentgrade = array(
1609              'userid' => $this->student->id,
1610              'rawgrade' => 10,
1611              'feedback' => '',
1612              'feedbackformat' => 1,
1613              'datesubmitted' => $timenow,
1614              'dategraded' => $timenow,
1615          );
1616  
1617          workshop_grade_item_update($this->workshop, (object) $submissiongrade, (object) $assessmentgrade);
1618  
1619          // First retrieve my grades.
1620          $this->setUser($this->student);
1621          $result = mod_workshop_external::get_grades($this->workshop->id);
1622          $result = \external_api::clean_returnvalue(mod_workshop_external::get_grades_returns(), $result);
1623          $this->assertCount(0, $result['warnings']);
1624          $this->assertEquals($assessmentgrade['rawgrade'], $result['assessmentrawgrade']);
1625          $this->assertEquals($submissiongrade['rawgrade'], $result['submissionrawgrade']);
1626          $this->assertFalse($result['assessmentgradehidden']);
1627          $this->assertFalse($result['submissiongradehidden']);
1628          $this->assertEquals($assessmentgrade['rawgrade'] . ".00 / 20.00", $result['assessmentlongstrgrade']);
1629          $this->assertEquals($submissiongrade['rawgrade'] . ".00 / 80.00", $result['submissionlongstrgrade']);
1630  
1631          // Second, teacher retrieve user grades.
1632          $this->setUser($this->teacher);
1633          $result = mod_workshop_external::get_grades($this->workshop->id, $this->student->id);
1634          $result = \external_api::clean_returnvalue(mod_workshop_external::get_grades_returns(), $result);
1635          $this->assertCount(0, $result['warnings']);
1636          $this->assertEquals($assessmentgrade['rawgrade'], $result['assessmentrawgrade']);
1637          $this->assertEquals($submissiongrade['rawgrade'], $result['submissionrawgrade']);
1638          $this->assertFalse($result['assessmentgradehidden']);
1639          $this->assertFalse($result['submissiongradehidden']);
1640          $this->assertEquals($assessmentgrade['rawgrade'] . ".00 / 20.00", $result['assessmentlongstrgrade']);
1641          $this->assertEquals($submissiongrade['rawgrade'] . ".00 / 80.00", $result['submissionlongstrgrade']);
1642      }
1643  
1644      /**
1645       * Test get_grades_other_student.
1646       */
1647      public function test_get_grades_other_student() {
1648  
1649          // Create the submission that will be deleted.
1650          $submissionid = $this->create_test_submission($this->student);
1651  
1652          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1653          $workshop->switch_phase(workshop::PHASE_CLOSED);
1654          $this->setUser($this->anotherstudentg1);
1655          $this->expectException('moodle_exception');
1656          mod_workshop_external::get_grades($this->workshop->id, $this->student->id);
1657      }
1658  
1659      /**
1660       * Test evaluate_assessment.
1661       */
1662      public function test_evaluate_assessment() {
1663          global $DB;
1664  
1665          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1666          $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1667          $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1668              'weight' => 3,
1669              'grade' => 20,
1670          ));
1671  
1672          $this->setUser($this->teacher);
1673          $feedbacktext = 'The feedback';
1674          $feedbackformat = FORMAT_MOODLE;
1675          $weight = 10;
1676          $gradinggradeover = 10;
1677          $result = mod_workshop_external::evaluate_assessment($assessmentid, $feedbacktext, $feedbackformat, $weight,
1678              $gradinggradeover);
1679          $result = \external_api::clean_returnvalue(mod_workshop_external::evaluate_assessment_returns(), $result);
1680          $this->assertTrue($result['status']);
1681  
1682          $assessment = $DB->get_record('workshop_assessments', array('id' => $assessmentid));
1683          $this->assertEquals('The feedback', $assessment->feedbackreviewer);
1684          $this->assertEquals(10, $assessment->weight);
1685  
1686          // Now test passing incorrect weight and grade values.
1687          $weight = 17;
1688          $gradinggradeover = 100;
1689          $result = mod_workshop_external::evaluate_assessment($assessmentid, $feedbacktext, $feedbackformat, $weight,
1690              $gradinggradeover);
1691          $result = \external_api::clean_returnvalue(mod_workshop_external::evaluate_assessment_returns(), $result);
1692          $this->assertFalse($result['status']);
1693          $this->assertCount(2, $result['warnings']);
1694          $found = 0;
1695          foreach ($result['warnings'] as $warning) {
1696              if ($warning['item'] == 'weight' || $warning['item'] == 'gradinggradeover') {
1697                  $found++;
1698              }
1699          }
1700          $this->assertEquals(2, $found);
1701      }
1702  
1703      /**
1704       * Test evaluate_assessment_ignore_parameters.
1705       */
1706      public function test_evaluate_assessment_ignore_parameters() {
1707          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1708          $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1709          $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1710              'weight' => 3,
1711              'grade' => 20,
1712          ));
1713  
1714          assign_capability('mod/workshop:allocate', CAP_PROHIBIT, $this->teacherrole->id, $this->context->id);
1715          // Empty all the caches that may be affected  by this change.
1716          accesslib_clear_all_caches_for_unit_testing();
1717  
1718          $this->setUser($this->teacher);
1719          $feedbacktext = 'The feedback';
1720          $feedbackformat = FORMAT_MOODLE;
1721          $weight = 10;
1722          $gradinggradeover = 19;
1723          $result = mod_workshop_external::evaluate_assessment($assessmentid, $feedbacktext, $feedbackformat, $weight,
1724              $gradinggradeover);
1725          $result = \external_api::clean_returnvalue(mod_workshop_external::evaluate_assessment_returns(), $result);
1726          $this->assertTrue($result['status']);
1727  
1728          $result = mod_workshop_external::get_assessment($assessmentid);
1729          $result = \external_api::clean_returnvalue(mod_workshop_external::get_assessment_returns(), $result);
1730          $this->assertNotEquals(10, $result['assessment']['weight']);
1731      }
1732  
1733      /**
1734       * Test evaluate_assessment_no_permissions.
1735       */
1736      public function test_evaluate_assessment_no_permissions() {
1737          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1738          $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1739          $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1740              'weight' => 3,
1741              'grade' => 20,
1742          ));
1743  
1744          $this->setUser($this->student);
1745          $feedbacktext = 'The feedback';
1746          $feedbackformat = FORMAT_MOODLE;
1747          $weight = 10;
1748          $gradinggradeover = 50;
1749          $this->expectException('moodle_exception');
1750          mod_workshop_external::evaluate_assessment($assessmentid, $feedbacktext, $feedbackformat, $weight, $gradinggradeover);
1751      }
1752  
1753      /**
1754       * Test get_grades_report.
1755       */
1756      public function test_get_grades_report() {
1757  
1758          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1759          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1760          $submissionid1 = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1761          $submissionid2 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id);
1762  
1763          $assessmentid1 = $workshopgenerator->create_assessment($submissionid2, $this->student->id, array(
1764              'weight' => 100,
1765              'grade' => 50,
1766          ));
1767          $assessmentid2 = $workshopgenerator->create_assessment($submissionid1, $this->anotherstudentg1->id, array(
1768              'weight' => 100,
1769              'grade' => 55,
1770          ));
1771  
1772          $workshop->switch_phase(workshop::PHASE_CLOSED);
1773          $this->setUser($this->teacher);
1774          $result = mod_workshop_external::get_grades_report($this->workshop->id);
1775          $result = \external_api::clean_returnvalue(mod_workshop_external::get_grades_report_returns(), $result);
1776          $this->assertEquals(3, $result['report']['totalcount']); // Expect 3 potential submissions.
1777  
1778          foreach ($result['report']['grades'] as $grade) {
1779              if ($grade['userid'] == $this->student->id) {
1780                  $this->assertEquals($this->anotherstudentg1->id, $grade['reviewedby'][0]['userid']); // Check reviewer.
1781                  $this->assertEquals($this->anotherstudentg1->id, $grade['reviewerof'][0]['userid']); // Check reviewer.
1782                  $this->assertEquals($workshop->real_grade(50), $grade['reviewerof'][0]['grade']); // Check grade (converted).
1783                  $this->assertEquals($workshop->real_grade(55), $grade['reviewedby'][0]['grade']); // Check grade (converted).
1784              } else if ($grade['userid'] == $this->anotherstudentg1->id) {
1785                  $this->assertEquals($this->student->id, $grade['reviewedby'][0]['userid']); // Check reviewer.
1786                  $this->assertEquals($this->student->id, $grade['reviewerof'][0]['userid']); // Check reviewer.
1787                  $this->assertEquals($workshop->real_grade(55), $grade['reviewerof'][0]['grade']); // Check grade (converted).
1788                  $this->assertEquals($workshop->real_grade(50), $grade['reviewedby'][0]['grade']); // Check grade (converted).
1789              }
1790          }
1791          // Now check pagination.
1792          $result = mod_workshop_external::get_grades_report($this->workshop->id, 0, 'lastname', 'ASC', 0, 1);
1793          $result = \external_api::clean_returnvalue(mod_workshop_external::get_grades_report_returns(), $result);
1794          $this->assertEquals(3, $result['report']['totalcount']); // Expect the total count.
1795          $this->assertCount(1, $result['report']['grades']);
1796  
1797          // Groups filtering.
1798          $result = mod_workshop_external::get_grades_report($this->workshop->id, $this->group1->id);
1799          $result = \external_api::clean_returnvalue(mod_workshop_external::get_grades_report_returns(), $result);
1800          $this->assertEquals(2, $result['report']['totalcount']); // Expect the group count.
1801      }
1802  
1803      /**
1804       * Test get_grades_report_invalid_phase.
1805       */
1806      public function test_get_grades_report_invalid_phase() {
1807          $this->setUser($this->teacher);
1808          $this->expectException('moodle_exception');
1809          $this->expectExceptionMessage(get_string('nothingfound', 'workshop'));
1810          mod_workshop_external::get_grades_report($this->workshop->id);
1811      }
1812  
1813      /**
1814       * Test get_grades_report_missing_permissions.
1815       */
1816      public function test_get_grades_report_missing_permissions() {
1817          $this->setUser($this->student);
1818          $this->expectException('required_capability_exception');
1819          mod_workshop_external::get_grades_report($this->workshop->id);
1820      }
1821  
1822      /**
1823       * Test test_view_submission.
1824       */
1825      public function test_view_submission() {
1826  
1827          // Create a couple of submissions with files.
1828          $firstsubmissionid = $this->create_test_submission($this->student);  // Create submission with files.
1829  
1830          // Trigger and capture the event.
1831          $sink = $this->redirectEvents();
1832  
1833          $this->setUser($this->student);
1834          $result = mod_workshop_external::view_submission($firstsubmissionid);
1835          $result = \external_api::clean_returnvalue(mod_workshop_external::view_submission_returns(), $result);
1836  
1837          $events = $sink->get_events();
1838          $this->assertCount(1, $events);
1839          $event = array_shift($events);
1840  
1841          // Checking that the event contains the expected values.
1842          $this->assertInstanceOf('\mod_workshop\event\submission_viewed', $event);
1843          $this->assertEquals($this->context, $event->get_context());
1844          $moodleworkshop = new \moodle_url('/mod/workshop/submission.php', array('id' => $firstsubmissionid,
1845              'cmid' => $this->cm->id));
1846          $this->assertEquals($moodleworkshop, $event->get_url());
1847          $this->assertEventContextNotUsed($event);
1848          $this->assertNotEmpty($event->get_name());
1849  
1850      }
1851  
1852      /**
1853       * Test evaluate_submission.
1854       */
1855      public function test_evaluate_submission() {
1856          global $DB;
1857  
1858          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1859          $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1860  
1861          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1862          $workshop->switch_phase(workshop::PHASE_EVALUATION);
1863  
1864          $this->setUser($this->teacher);
1865          $feedbacktext = 'The feedback';
1866          $feedbackformat = FORMAT_MOODLE;
1867          $published = 1;
1868          $gradeover = 10;
1869          $result = mod_workshop_external::evaluate_submission($submissionid, $feedbacktext, $feedbackformat, $published,
1870              $gradeover);
1871          $result = \external_api::clean_returnvalue(mod_workshop_external::evaluate_submission_returns(), $result);
1872          $this->assertTrue($result['status']);
1873  
1874          $submission = $DB->get_record('workshop_submissions', array('id' => $submissionid));
1875          $this->assertEquals($feedbacktext, $submission->feedbackauthor);
1876          $this->assertEquals($workshop->raw_grade_value($gradeover, $workshop->grade), $submission->gradeover);  // Expected grade.
1877          $this->assertEquals(1, $submission->published); // Submission published.
1878      }
1879  
1880      /**
1881       * Test evaluate_submission_invalid_phase_for_override.
1882       */
1883      public function test_evaluate_submission_invalid_phase_for_override() {
1884          global $DB;
1885  
1886          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1887          $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1888  
1889          $this->setUser($this->teacher);
1890          $feedbacktext = 'The feedback';
1891          $feedbackformat = FORMAT_MOODLE;
1892          $published = 1;
1893          $gradeover = 10;
1894          $result = mod_workshop_external::evaluate_submission($submissionid, $feedbacktext, $feedbackformat, $published,
1895              $gradeover);
1896          $result = \external_api::clean_returnvalue(mod_workshop_external::evaluate_submission_returns(), $result);
1897          $this->assertTrue($result['status']);
1898  
1899          $submission = $DB->get_record('workshop_submissions', array('id' => $submissionid));
1900          $this->assertEquals('', $submission->feedbackauthor);   // Feedback and grade not updated.
1901          $this->assertEquals(0, $submission->gradeover);
1902          $this->assertEquals(1, $submission->published); // Publishing status correctly updated.
1903      }
1904  
1905      /**
1906       * Test evaluate_submission_no_permissions.
1907       */
1908      public function test_evaluate_submission_no_permissions() {
1909  
1910          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1911          $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1912          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1913          $workshop->switch_phase(workshop::PHASE_EVALUATION);
1914  
1915          $this->setUser($this->student);
1916          $feedbacktext = 'The feedback';
1917          $feedbackformat = FORMAT_MOODLE;
1918          $published = 1;
1919          $gradeover = 50;
1920          $this->expectException('moodle_exception');
1921          mod_workshop_external::evaluate_submission($submissionid, $feedbacktext, $feedbackformat, $published, $gradeover);
1922      }
1923  
1924      /**
1925       * Test evaluate_submission_invalid_grade.
1926       */
1927      public function test_evaluate_submission_invalid_grade() {
1928  
1929          $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1930          $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1931          $workshop = new workshop($this->workshop, $this->cm, $this->course);
1932          $workshop->switch_phase(workshop::PHASE_EVALUATION);
1933  
1934          $this->setUser($this->teacher);
1935          $feedbacktext = 'The feedback';
1936          $feedbackformat = FORMAT_MOODLE;
1937          $published = 1;
1938          $gradeover = 150;
1939          $result = mod_workshop_external::evaluate_submission($submissionid, $feedbacktext, $feedbackformat, $published, $gradeover);
1940          $result = \external_api::clean_returnvalue(mod_workshop_external::evaluate_submission_returns(), $result);
1941          $this->assertCount(1, $result['warnings']);
1942          $this->assertFalse($result['status']);
1943          $this->assertEquals('gradeover', $result['warnings'][0]['item']);
1944      }
1945  }