Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310]

   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   * Unit tests for assignfeedback_editpdf\comments_quick_list
  19   *
  20   * @package    assignfeedback_editpdf
  21   * @category   phpunit
  22   * @copyright  2013 Damyon Wiese
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  use \assignfeedback_editpdf\comments_quick_list;
  29  use \assignfeedback_editpdf\document_services;
  30  use \assignfeedback_editpdf\page_editor;
  31  use \assignfeedback_editpdf\pdf;
  32  use \assignfeedback_editpdf\comment;
  33  use \assignfeedback_editpdf\annotation;
  34  
  35  global $CFG;
  36  require_once($CFG->dirroot . '/mod/assign/tests/generator.php');
  37  
  38  /**
  39   * Unit tests for assignfeedback_editpdf\comments_quick_list
  40   *
  41   * @copyright  2013 Damyon Wiese
  42   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  43   */
  44  class assignfeedback_editpdf_testcase extends advanced_testcase {
  45  
  46      // Use the generator helper.
  47      use mod_assign_test_generator;
  48  
  49      /**
  50       * Ensure that GS is available.
  51       */
  52      protected function require_ghostscript() {
  53          // Skip this test if ghostscript is not supported.
  54          $result = pdf::test_gs_path(false);
  55          if ($result->status !== assignfeedback_editpdf\pdf::GSPATH_OK) {
  56              $this->markTestSkipped('Ghostscript not setup');
  57          }
  58      }
  59  
  60      protected function add_file_submission($student, $assign) {
  61          global $CFG;
  62  
  63          $this->setUser($student);
  64  
  65          // Create a file submission with the test pdf.
  66          $submission = $assign->get_user_submission($student->id, true);
  67  
  68          $fs = get_file_storage();
  69          $pdfsubmission = (object) array(
  70              'contextid' => $assign->get_context()->id,
  71              'component' => 'assignsubmission_file',
  72              'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
  73              'itemid' => $submission->id,
  74              'filepath' => '/',
  75              'filename' => 'submission.pdf'
  76          );
  77          $sourcefile = $CFG->dirroot.'/mod/assign/feedback/editpdf/tests/fixtures/submission.pdf';
  78          $fs->create_file_from_pathname($pdfsubmission, $sourcefile);
  79  
  80          $data = new stdClass();
  81          $plugin = $assign->get_submission_plugin_by_type('file');
  82          $plugin->save($submission, $data);
  83      }
  84  
  85      public function test_comments_quick_list() {
  86          $this->resetAfterTest();
  87          $course = $this->getDataGenerator()->create_course();
  88          $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
  89  
  90          $this->setUser($teacher);
  91  
  92          $this->assertEmpty(comments_quick_list::get_comments());
  93  
  94          $comment = comments_quick_list::add_comment('test', 45, 'red');
  95          $comments = comments_quick_list::get_comments();
  96          $this->assertEquals(count($comments), 1);
  97          $first = reset($comments);
  98          $this->assertEquals($comment, $first);
  99  
 100          $commentbyid = comments_quick_list::get_comment($comment->id);
 101          $this->assertEquals($comment, $commentbyid);
 102  
 103          $this->assertTrue(comments_quick_list::remove_comment($comment->id));
 104  
 105          $comments = comments_quick_list::get_comments();
 106          $this->assertEmpty($comments);
 107      }
 108  
 109      public function test_page_editor() {
 110          $this->resetAfterTest();
 111          $course = $this->getDataGenerator()->create_course();
 112          $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
 113          $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
 114          $assign = $this->create_instance($course, [
 115                  'assignsubmission_onlinetext_enabled' => 1,
 116                  'assignsubmission_file_enabled' => 1,
 117                  'assignsubmission_file_maxfiles' => 1,
 118                  'assignfeedback_editpdf_enabled' => 1,
 119                  'assignsubmission_file_maxsizebytes' => 1000000,
 120              ]);
 121  
 122          // Add the standard submission.
 123          $this->add_file_submission($student, $assign);
 124  
 125          $this->setUser($teacher);
 126  
 127          $grade = $assign->get_user_grade($student->id, true);
 128  
 129          $notempty = page_editor::has_annotations_or_comments($grade->id, false);
 130          $this->assertFalse($notempty);
 131  
 132          $comment = new comment();
 133          $comment->rawtext = 'Comment text';
 134          $comment->width = 100;
 135          $comment->x = 100;
 136          $comment->y = 100;
 137          $comment->colour = 'red';
 138  
 139          $comment2 = new comment();
 140          $comment2->rawtext = 'Comment text 2';
 141          $comment2->width = 100;
 142          $comment2->x = 200;
 143          $comment2->y = 100;
 144          $comment2->colour = 'clear';
 145  
 146          page_editor::set_comments($grade->id, 0, array($comment, $comment2));
 147  
 148          $annotation = new annotation();
 149          $annotation->path = '';
 150          $annotation->x = 100;
 151          $annotation->y = 100;
 152          $annotation->endx = 200;
 153          $annotation->endy = 200;
 154          $annotation->type = 'line';
 155          $annotation->colour = 'red';
 156  
 157          $annotation2 = new annotation();
 158          $annotation2->path = '';
 159          $annotation2->x = 100;
 160          $annotation2->y = 100;
 161          $annotation2->endx = 200;
 162          $annotation2->endy = 200;
 163          $annotation2->type = 'rectangle';
 164          $annotation2->colour = 'yellow';
 165  
 166          page_editor::set_annotations($grade->id, 0, array($annotation, $annotation2));
 167  
 168          // Still empty because all edits are still drafts.
 169          $this->assertFalse(page_editor::has_annotations_or_comments($grade->id, false));
 170  
 171          $comments = page_editor::get_comments($grade->id, 0, false);
 172          $this->assertEmpty($comments);
 173  
 174          $comments = page_editor::get_comments($grade->id, 0, true);
 175          $this->assertEquals(count($comments), 2);
 176  
 177          $annotations = page_editor::get_annotations($grade->id, 0, false);
 178          $this->assertEmpty($annotations);
 179  
 180          $annotations = page_editor::get_annotations($grade->id, 0, true);
 181          $this->assertEquals(count($annotations), 2);
 182  
 183          $comment = reset($comments);
 184          $annotation = reset($annotations);
 185  
 186          page_editor::remove_comment($comment->id);
 187          page_editor::remove_annotation($annotation->id);
 188  
 189          $comments = page_editor::get_comments($grade->id, 0, true);
 190          $this->assertEquals(count($comments), 1);
 191  
 192          $annotations = page_editor::get_annotations($grade->id, 0, true);
 193          $this->assertEquals(count($annotations), 1);
 194  
 195          // Release the drafts.
 196          page_editor::release_drafts($grade->id);
 197  
 198          $notempty = page_editor::has_annotations_or_comments($grade->id, false);
 199          $this->assertTrue($notempty);
 200  
 201          // Unrelease the drafts.
 202          page_editor::unrelease_drafts($grade->id);
 203  
 204          $notempty = page_editor::has_annotations_or_comments($grade->id, false);
 205          $this->assertFalse($notempty);
 206      }
 207  
 208      public function test_document_services() {
 209          $this->require_ghostscript();
 210          $this->resetAfterTest();
 211          $course = $this->getDataGenerator()->create_course();
 212          $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
 213          $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
 214          $assign = $this->create_instance($course, [
 215                  'assignsubmission_onlinetext_enabled' => 1,
 216                  'assignsubmission_file_enabled' => 1,
 217                  'assignsubmission_file_maxfiles' => 1,
 218                  'assignfeedback_editpdf_enabled' => 1,
 219                  'assignsubmission_file_maxsizebytes' => 1000000,
 220              ]);
 221  
 222          // Add the standard submission.
 223          $this->add_file_submission($student, $assign);
 224  
 225          $this->setUser($teacher);
 226  
 227          $grade = $assign->get_user_grade($student->id, true);
 228  
 229          $contextid = $assign->get_context()->id;
 230          $component = 'assignfeedback_editpdf';
 231          $filearea = document_services::COMBINED_PDF_FILEAREA;
 232          $itemid = $grade->id;
 233          $filepath = '/';
 234          $filename = document_services::COMBINED_PDF_FILENAME;
 235          $fs = \get_file_storage();
 236  
 237          // Generate a blank combined pdf.
 238          $record = new \stdClass();
 239          $record->contextid = $contextid;
 240          $record->component = $component;
 241          $record->filearea = $filearea;
 242          $record->itemid = $itemid;
 243          $record->filepath = $filepath;
 244          $record->filename = $filename;
 245          $fs->create_file_from_string($record, base64_decode(document_services::BLANK_PDF_BASE64));
 246  
 247          // Verify that the blank combined pdf has the expected hash.
 248          $combinedpdf = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename);
 249          $this->assertEquals($combinedpdf->get_contenthash(), document_services::BLANK_PDF_HASH);
 250  
 251          // Generate page images and verify that the combined pdf has been replaced.
 252          document_services::get_page_images_for_attempt($assign, $student->id, -1);
 253          $combinedpdf = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename);
 254          $this->assertNotEquals($combinedpdf->get_contenthash(), document_services::BLANK_PDF_HASH);
 255  
 256          $notempty = page_editor::has_annotations_or_comments($grade->id, false);
 257          $this->assertFalse($notempty);
 258  
 259          $comment = new comment();
 260  
 261          // Use some different charset in the comment text.
 262          $comment->rawtext = 'Testing example: בקלות ואמנות';
 263          $comment->width = 100;
 264          $comment->x = 100;
 265          $comment->y = 100;
 266          $comment->colour = 'red';
 267  
 268          page_editor::set_comments($grade->id, 0, array($comment));
 269  
 270          $annotations = array();
 271  
 272          $annotation = new annotation();
 273          $annotation->path = '';
 274          $annotation->x = 100;
 275          $annotation->y = 100;
 276          $annotation->endx = 200;
 277          $annotation->endy = 200;
 278          $annotation->type = 'line';
 279          $annotation->colour = 'red';
 280          array_push($annotations, $annotation);
 281  
 282          $annotation = new annotation();
 283          $annotation->path = '';
 284          $annotation->x = 100;
 285          $annotation->y = 100;
 286          $annotation->endx = 200;
 287          $annotation->endy = 200;
 288          $annotation->type = 'rectangle';
 289          $annotation->colour = 'yellow';
 290          array_push($annotations, $annotation);
 291  
 292          $annotation = new annotation();
 293          $annotation->path = '';
 294          $annotation->x = 100;
 295          $annotation->y = 100;
 296          $annotation->endx = 200;
 297          $annotation->endy = 200;
 298          $annotation->type = 'oval';
 299          $annotation->colour = 'green';
 300          array_push($annotations, $annotation);
 301  
 302          $annotation = new annotation();
 303          $annotation->path = '';
 304          $annotation->x = 100;
 305          $annotation->y = 100;
 306          $annotation->endx = 200;
 307          $annotation->endy = 116;
 308          $annotation->type = 'highlight';
 309          $annotation->colour = 'blue';
 310          array_push($annotations, $annotation);
 311  
 312          $annotation = new annotation();
 313          $annotation->path = '100,100:105,105:110,100';
 314          $annotation->x = 100;
 315          $annotation->y = 100;
 316          $annotation->endx = 110;
 317          $annotation->endy = 105;
 318          $annotation->type = 'pen';
 319          $annotation->colour = 'black';
 320          array_push($annotations, $annotation);
 321          page_editor::set_annotations($grade->id, 0, $annotations);
 322  
 323          page_editor::release_drafts($grade->id);
 324  
 325          $notempty = page_editor::has_annotations_or_comments($grade->id, false);
 326  
 327          $this->assertTrue($notempty);
 328  
 329          $file = document_services::generate_feedback_document($assign->get_instance()->id, $grade->userid, $grade->attemptnumber);
 330          $this->assertNotEmpty($file);
 331  
 332          $file2 = document_services::get_feedback_document($assign->get_instance()->id, $grade->userid, $grade->attemptnumber);
 333  
 334          $this->assertEquals($file, $file2);
 335  
 336          document_services::delete_feedback_document($assign->get_instance()->id, $grade->userid, $grade->attemptnumber);
 337          $file3 = document_services::get_feedback_document($assign->get_instance()->id, $grade->userid, $grade->attemptnumber);
 338  
 339          $this->assertEmpty($file3);
 340      }
 341  
 342      public function test_conversion_task() {
 343          global $DB;
 344          $this->require_ghostscript();
 345          $this->resetAfterTest();
 346          cron_setup_user();
 347  
 348          $task = new \assignfeedback_editpdf\task\convert_submissions;
 349  
 350          $course = $this->getDataGenerator()->create_course();
 351          $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
 352          $assignopts = [
 353              'assignsubmission_file_enabled' => 1,
 354              'assignsubmission_file_maxfiles' => 1,
 355              'assignfeedback_editpdf_enabled' => 1,
 356              'assignsubmission_file_maxsizebytes' => 1000000,
 357          ];
 358          $assign = $this->create_instance($course, $assignopts);
 359  
 360          // Add the standard submission.
 361          $this->add_file_submission($student, $assign);
 362  
 363          // Run the conversion task.
 364          ob_start();
 365          $task->execute();
 366          $output = ob_get_clean();
 367  
 368          // Verify it acted on both submissions in the queue.
 369          $this->assertContains("Convert 1 submission attempt(s) for assignment {$assign->get_instance()->id}", $output);
 370          $this->assertEquals(0, $DB->count_records('assignfeedback_editpdf_queue'));
 371  
 372          // Set a known limit.
 373          set_config('conversionattemptlimit', 3);
 374  
 375          // Trigger a re-queue by 'updating' a submission.
 376          $submission = $assign->get_user_submission($student->id, true);
 377          $plugin = $assign->get_submission_plugin_by_type('file');
 378          $plugin->save($submission, (new stdClass));
 379  
 380          // Verify that queued a conversion task.
 381          $this->assertEquals(1, $DB->count_records('assignfeedback_editpdf_queue'));
 382  
 383          // Fake some failed attempts for it.
 384          $queuerecord = $DB->get_record('assignfeedback_editpdf_queue', ['submissionid' => $submission->id]);
 385          $queuerecord->attemptedconversions = 3;
 386          $DB->update_record('assignfeedback_editpdf_queue', $queuerecord);
 387  
 388          ob_start();
 389          $task->execute();
 390          $output = ob_get_clean();
 391  
 392          // Verify that the cron task skipped the submission.
 393          $this->assertNotContains("Convert 1 submission attempt(s) for assignment {$assign->get_instance()->id}", $output);
 394          // And it removed it from the queue.
 395          $this->assertEquals(0, $DB->count_records('assignfeedback_editpdf_queue'));
 396  
 397      }
 398  
 399      /**
 400       * Test that modifying the annotated pdf form return true when modified
 401       * and false when not modified.
 402       */
 403      public function test_is_feedback_modified() {
 404          $this->resetAfterTest();
 405          $course = $this->getDataGenerator()->create_course();
 406          $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
 407          $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
 408          $assign = $this->create_instance($course, [
 409                  'assignsubmission_onlinetext_enabled' => 1,
 410                  'assignsubmission_file_enabled' => 1,
 411                  'assignsubmission_file_maxfiles' => 1,
 412                  'assignfeedback_editpdf_enabled' => 1,
 413                  'assignsubmission_file_maxsizebytes' => 1000000,
 414              ]);
 415  
 416          // Add the standard submission.
 417          $this->add_file_submission($student, $assign);
 418  
 419          $this->setUser($teacher);
 420          $grade = $assign->get_user_grade($student->id, true);
 421  
 422          $notempty = page_editor::has_annotations_or_comments($grade->id, false);
 423          $this->assertFalse($notempty);
 424  
 425          $comment = new comment();
 426  
 427          $comment->rawtext = 'Comment text';
 428          $comment->width = 100;
 429          $comment->x = 100;
 430          $comment->y = 100;
 431          $comment->colour = 'red';
 432  
 433          page_editor::set_comments($grade->id, 0, array($comment));
 434  
 435          $annotations = array();
 436  
 437          $annotation = new annotation();
 438          $annotation->path = '';
 439          $annotation->x = 100;
 440          $annotation->y = 100;
 441          $annotation->endx = 200;
 442          $annotation->endy = 200;
 443          $annotation->type = 'line';
 444          $annotation->colour = 'red';
 445          array_push($annotations, $annotation);
 446  
 447          page_editor::set_annotations($grade->id, 0, $annotations);
 448  
 449          $plugin = $assign->get_feedback_plugin_by_type('editpdf');
 450          $data = new stdClass();
 451          $data->editpdf_source_userid = $student->id;
 452          $this->assertTrue($plugin->is_feedback_modified($grade, $data));
 453          $plugin->save($grade, $data);
 454  
 455          $annotation = new annotation();
 456          $annotation->gradeid = $grade->id;
 457          $annotation->pageno = 0;
 458          $annotation->path = '';
 459          $annotation->x = 100;
 460          $annotation->y = 100;
 461          $annotation->endx = 200;
 462          $annotation->endy = 200;
 463          $annotation->type = 'rectangle';
 464          $annotation->colour = 'yellow';
 465  
 466          $yellowannotationid = page_editor::add_annotation($annotation);
 467  
 468          // Add a comment as well.
 469          $comment = new comment();
 470          $comment->gradeid = $grade->id;
 471          $comment->pageno = 0;
 472          $comment->rawtext = 'Second Comment text';
 473          $comment->width = 100;
 474          $comment->x = 100;
 475          $comment->y = 100;
 476          $comment->colour = 'red';
 477          page_editor::add_comment($comment);
 478  
 479          $this->assertTrue($plugin->is_feedback_modified($grade, $data));
 480          $plugin->save($grade, $data);
 481  
 482          // We should have two annotations.
 483          $this->assertCount(2, page_editor::get_annotations($grade->id, 0, false));
 484          // And two comments.
 485          $this->assertCount(2, page_editor::get_comments($grade->id, 0, false));
 486  
 487          // Add one annotation and delete another.
 488          $annotation = new annotation();
 489          $annotation->gradeid = $grade->id;
 490          $annotation->pageno = 0;
 491          $annotation->path = '100,100:105,105:110,100';
 492          $annotation->x = 100;
 493          $annotation->y = 100;
 494          $annotation->endx = 110;
 495          $annotation->endy = 105;
 496          $annotation->type = 'pen';
 497          $annotation->colour = 'black';
 498          page_editor::add_annotation($annotation);
 499  
 500          $annotations = page_editor::get_annotations($grade->id, 0, true);
 501          page_editor::remove_annotation($yellowannotationid);
 502          $this->assertTrue($plugin->is_feedback_modified($grade, $data));
 503          $plugin->save($grade, $data);
 504  
 505          // We should have two annotations.
 506          $this->assertCount(2, page_editor::get_annotations($grade->id, 0, false));
 507          // And two comments.
 508          $this->assertCount(2, page_editor::get_comments($grade->id, 0, false));
 509  
 510          // Add a comment and then remove it. Should not be considered as modified.
 511          $comment = new comment();
 512          $comment->gradeid = $grade->id;
 513          $comment->pageno = 0;
 514          $comment->rawtext = 'Third Comment text';
 515          $comment->width = 400;
 516          $comment->x = 57;
 517          $comment->y = 205;
 518          $comment->colour = 'black';
 519          $comment->id = page_editor::add_comment($comment);
 520  
 521          // We should now have three comments.
 522          $this->assertCount(3, page_editor::get_comments($grade->id, 0, true));
 523          // Now delete the newest record.
 524          page_editor::remove_comment($comment->id);
 525          // Back to two comments.
 526          $this->assertCount(2, page_editor::get_comments($grade->id, 0, true));
 527          // No modification.
 528          $this->assertFalse($plugin->is_feedback_modified($grade, $data));
 529      }
 530  }