Search moodle.org's
Developer Documentation

See Release Notes

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

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