Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
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 * This file contains the definition for the library class for comment feedback plugin 19 * 20 * @package assignfeedback_comments 21 * @copyright 2012 NetSpot {@link http://www.netspot.com.au} 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 use core_external\external_single_structure; 26 use core_external\external_value; 27 28 defined('MOODLE_INTERNAL') || die(); 29 30 // File component for feedback comments. 31 define('ASSIGNFEEDBACK_COMMENTS_COMPONENT', 'assignfeedback_comments'); 32 33 // File area for feedback comments. 34 define('ASSIGNFEEDBACK_COMMENTS_FILEAREA', 'feedback'); 35 36 /** 37 * Library class for comment feedback plugin extending feedback plugin base class. 38 * 39 * @package assignfeedback_comments 40 * @copyright 2012 NetSpot {@link http://www.netspot.com.au} 41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 42 */ 43 class assign_feedback_comments extends assign_feedback_plugin { 44 45 /** 46 * Get the name of the online comment feedback plugin. 47 * @return string 48 */ 49 public function get_name() { 50 return get_string('pluginname', 'assignfeedback_comments'); 51 } 52 53 /** 54 * Get the feedback comment from the database. 55 * 56 * @param int $gradeid 57 * @return stdClass|false The feedback comments for the given grade if it exists. 58 * False if it doesn't. 59 */ 60 public function get_feedback_comments($gradeid) { 61 global $DB; 62 return $DB->get_record('assignfeedback_comments', array('grade'=>$gradeid)); 63 } 64 65 /** 66 * Get quickgrading form elements as html. 67 * 68 * @param int $userid The user id in the table this quickgrading element relates to 69 * @param mixed $grade - The grade data - may be null if there are no grades for this user (yet) 70 * @return mixed - A html string containing the html form elements required for quickgrading 71 */ 72 public function get_quickgrading_html($userid, $grade) { 73 $commenttext = ''; 74 if ($grade) { 75 $feedbackcomments = $this->get_feedback_comments($grade->id); 76 if ($feedbackcomments) { 77 $commenttext = $feedbackcomments->commenttext; 78 } 79 } 80 81 $pluginname = get_string('pluginname', 'assignfeedback_comments'); 82 $labeloptions = array('for'=>'quickgrade_comments_' . $userid, 83 'class'=>'accesshide'); 84 $textareaoptions = array('name'=>'quickgrade_comments_' . $userid, 85 'id'=>'quickgrade_comments_' . $userid, 86 'class'=>'quickgrade'); 87 return html_writer::tag('label', $pluginname, $labeloptions) . 88 html_writer::tag('textarea', $commenttext, $textareaoptions); 89 } 90 91 /** 92 * Has the plugin quickgrading form element been modified in the current form submission? 93 * 94 * @param int $userid The user id in the table this quickgrading element relates to 95 * @param stdClass $grade The grade 96 * @return boolean - true if the quickgrading form element has been modified 97 */ 98 public function is_quickgrading_modified($userid, $grade) { 99 $commenttext = ''; 100 if ($grade) { 101 $feedbackcomments = $this->get_feedback_comments($grade->id); 102 if ($feedbackcomments) { 103 $commenttext = $feedbackcomments->commenttext; 104 } 105 } 106 // Note that this handles the difference between empty and not in the quickgrading 107 // form at all (hidden column). 108 $newvalue = optional_param('quickgrade_comments_' . $userid, false, PARAM_RAW); 109 return ($newvalue !== false) && ($newvalue != $commenttext); 110 } 111 112 /** 113 * Has the comment feedback been modified? 114 * 115 * @param stdClass $grade The grade object. 116 * @param stdClass $data Data from the form submission. 117 * @return boolean True if the comment feedback has been modified, else false. 118 */ 119 public function is_feedback_modified(stdClass $grade, stdClass $data) { 120 $commenttext = ''; 121 if ($grade) { 122 $feedbackcomments = $this->get_feedback_comments($grade->id); 123 if ($feedbackcomments) { 124 $commenttext = $feedbackcomments->commenttext; 125 } 126 } 127 128 $formtext = $data->assignfeedbackcomments_editor['text']; 129 130 // Need to convert the form text to use @@PLUGINFILE@@ and format it so we can compare it with what is stored in the DB. 131 if (isset($data->assignfeedbackcomments_editor['itemid'])) { 132 $formtext = file_rewrite_urls_to_pluginfile($formtext, $data->assignfeedbackcomments_editor['itemid']); 133 $formtext = format_text($formtext, FORMAT_HTML); 134 } 135 136 if ($commenttext == $formtext) { 137 return false; 138 } else { 139 return true; 140 } 141 } 142 143 144 /** 145 * Override to indicate a plugin supports quickgrading. 146 * 147 * @return boolean - True if the plugin supports quickgrading 148 */ 149 public function supports_quickgrading() { 150 return true; 151 } 152 153 /** 154 * Return a list of the text fields that can be imported/exported by this plugin. 155 * 156 * @return array An array of field names and descriptions. (name=>description, ...) 157 */ 158 public function get_editor_fields() { 159 return array('comments' => get_string('pluginname', 'assignfeedback_comments')); 160 } 161 162 /** 163 * Get the saved text content from the editor. 164 * 165 * @param string $name 166 * @param int $gradeid 167 * @return string 168 */ 169 public function get_editor_text($name, $gradeid) { 170 if ($name == 'comments') { 171 $feedbackcomments = $this->get_feedback_comments($gradeid); 172 if ($feedbackcomments) { 173 return $feedbackcomments->commenttext; 174 } 175 } 176 177 return ''; 178 } 179 180 /** 181 * Get the saved text content from the editor. 182 * 183 * @param string $name 184 * @param string $value 185 * @param int $gradeid 186 * @return string 187 */ 188 public function set_editor_text($name, $value, $gradeid) { 189 global $DB; 190 191 if ($name == 'comments') { 192 $feedbackcomment = $this->get_feedback_comments($gradeid); 193 if ($feedbackcomment) { 194 $feedbackcomment->commenttext = $value; 195 return $DB->update_record('assignfeedback_comments', $feedbackcomment); 196 } else { 197 $feedbackcomment = new stdClass(); 198 $feedbackcomment->commenttext = $value; 199 $feedbackcomment->commentformat = FORMAT_HTML; 200 $feedbackcomment->grade = $gradeid; 201 $feedbackcomment->assignment = $this->assignment->get_instance()->id; 202 return $DB->insert_record('assignfeedback_comments', $feedbackcomment) > 0; 203 } 204 } 205 206 return false; 207 } 208 209 /** 210 * Save quickgrading changes. 211 * 212 * @param int $userid The user id in the table this quickgrading element relates to 213 * @param stdClass $grade The grade 214 * @return boolean - true if the grade changes were saved correctly 215 */ 216 public function save_quickgrading_changes($userid, $grade) { 217 global $DB; 218 $feedbackcomment = $this->get_feedback_comments($grade->id); 219 $quickgradecomments = optional_param('quickgrade_comments_' . $userid, null, PARAM_RAW); 220 if (!$quickgradecomments && $quickgradecomments !== '') { 221 return true; 222 } 223 if ($feedbackcomment) { 224 $feedbackcomment->commenttext = $quickgradecomments; 225 return $DB->update_record('assignfeedback_comments', $feedbackcomment); 226 } else { 227 $feedbackcomment = new stdClass(); 228 $feedbackcomment->commenttext = $quickgradecomments; 229 $feedbackcomment->commentformat = FORMAT_HTML; 230 $feedbackcomment->grade = $grade->id; 231 $feedbackcomment->assignment = $this->assignment->get_instance()->id; 232 return $DB->insert_record('assignfeedback_comments', $feedbackcomment) > 0; 233 } 234 } 235 236 /** 237 * Save the settings for feedback comments plugin 238 * 239 * @param stdClass $data 240 * @return bool 241 */ 242 public function save_settings(stdClass $data) { 243 $this->set_config('commentinline', !empty($data->assignfeedback_comments_commentinline)); 244 return true; 245 } 246 247 /** 248 * Get the default setting for feedback comments plugin 249 * 250 * @param MoodleQuickForm $mform The form to add elements to 251 * @return void 252 */ 253 public function get_settings(MoodleQuickForm $mform) { 254 $default = $this->get_config('commentinline'); 255 if ($default === false) { 256 // Apply the admin default if we don't have a value yet. 257 $default = get_config('assignfeedback_comments', 'inline'); 258 } 259 $mform->addElement('selectyesno', 260 'assignfeedback_comments_commentinline', 261 get_string('commentinline', 'assignfeedback_comments')); 262 $mform->addHelpButton('assignfeedback_comments_commentinline', 'commentinline', 'assignfeedback_comments'); 263 $mform->setDefault('assignfeedback_comments_commentinline', $default); 264 // Disable comment online if comment feedback plugin is disabled. 265 $mform->hideIf('assignfeedback_comments_commentinline', 'assignfeedback_comments_enabled', 'notchecked'); 266 } 267 268 /** 269 * Convert the text from any submission plugin that has an editor field to 270 * a format suitable for inserting in the feedback text field. 271 * 272 * @param stdClass $submission 273 * @param stdClass $data - Form data to be filled with the converted submission text and format. 274 * @param stdClass|null $grade 275 * @return boolean - True if feedback text was set. 276 */ 277 protected function convert_submission_text_to_feedback($submission, $data, $grade) { 278 global $DB; 279 280 $format = false; 281 $text = ''; 282 283 foreach ($this->assignment->get_submission_plugins() as $plugin) { 284 $fields = $plugin->get_editor_fields(); 285 if ($plugin->is_enabled() && $plugin->is_visible() && !$plugin->is_empty($submission) && !empty($fields)) { 286 $user = $DB->get_record('user', ['id' => $submission->userid]); 287 // Copy the files to the feedback area. 288 if ($files = $plugin->get_files($submission, $user)) { 289 $fs = get_file_storage(); 290 $component = 'assignfeedback_comments'; 291 $filearea = ASSIGNFEEDBACK_COMMENTS_FILEAREA; 292 $itemid = $grade->id; 293 $fieldupdates = [ 294 'component' => $component, 295 'filearea' => $filearea, 296 'itemid' => $itemid 297 ]; 298 foreach ($files as $file) { 299 if ($file instanceof stored_file) { 300 // Before we create it, check that it doesn't already exist. 301 if (!$fs->file_exists( 302 $file->get_contextid(), 303 $component, 304 $filearea, 305 $itemid, 306 $file->get_filepath(), 307 $file->get_filename())) { 308 $fs->create_file_from_storedfile($fieldupdates, $file); 309 } 310 } 311 } 312 } 313 foreach ($fields as $key => $description) { 314 $rawtext = clean_text($plugin->get_editor_text($key, $submission->id)); 315 $newformat = $plugin->get_editor_format($key, $submission->id); 316 317 if ($format !== false && $newformat != $format) { 318 // There are 2 or more editor fields using different formats, set to plain as a fallback. 319 $format = FORMAT_PLAIN; 320 } else { 321 $format = $newformat; 322 } 323 $text .= $rawtext; 324 } 325 } 326 } 327 328 if ($format === false) { 329 $format = FORMAT_HTML; 330 } 331 $data->assignfeedbackcomments = $text; 332 $data->assignfeedbackcommentsformat = $format; 333 334 return true; 335 } 336 337 /** 338 * Get form elements for the grading page 339 * 340 * @param stdClass|null $grade 341 * @param MoodleQuickForm $mform 342 * @param stdClass $data 343 * @return bool true if elements were added to the form 344 */ 345 public function get_form_elements_for_user($grade, MoodleQuickForm $mform, stdClass $data, $userid) { 346 $commentinlinenabled = $this->get_config('commentinline'); 347 $submission = $this->assignment->get_user_submission($userid, false); 348 $feedbackcomments = false; 349 350 if ($grade) { 351 $feedbackcomments = $this->get_feedback_comments($grade->id); 352 } 353 354 // Check first for data from last form submission in case grading validation failed. 355 if (!empty($data->assignfeedbackcomments_editor['text'])) { 356 $data->assignfeedbackcomments = $data->assignfeedbackcomments_editor['text']; 357 $data->assignfeedbackcommentsformat = $data->assignfeedbackcomments_editor['format']; 358 } else if ($feedbackcomments && !empty($feedbackcomments->commenttext)) { 359 $data->assignfeedbackcomments = $feedbackcomments->commenttext; 360 $data->assignfeedbackcommentsformat = $feedbackcomments->commentformat; 361 } else { 362 // No feedback given yet - maybe we need to copy the text from the submission? 363 if (!empty($commentinlinenabled) && $submission) { 364 $this->convert_submission_text_to_feedback($submission, $data, $grade); 365 } else { // Set it to empty. 366 $data->assignfeedbackcomments = ''; 367 $data->assignfeedbackcommentsformat = FORMAT_HTML; 368 } 369 } 370 371 file_prepare_standard_editor( 372 $data, 373 'assignfeedbackcomments', 374 $this->get_editor_options(), 375 $this->assignment->get_context(), 376 ASSIGNFEEDBACK_COMMENTS_COMPONENT, 377 ASSIGNFEEDBACK_COMMENTS_FILEAREA, 378 $grade->id 379 ); 380 381 $mform->addElement('editor', 'assignfeedbackcomments_editor', $this->get_name(), null, $this->get_editor_options()); 382 383 return true; 384 } 385 386 /** 387 * Saving the comment content into database. 388 * 389 * @param stdClass $grade 390 * @param stdClass $data 391 * @return bool 392 */ 393 public function save(stdClass $grade, stdClass $data) { 394 global $DB; 395 396 // Save the files. 397 $data = file_postupdate_standard_editor( 398 $data, 399 'assignfeedbackcomments', 400 $this->get_editor_options(), 401 $this->assignment->get_context(), 402 ASSIGNFEEDBACK_COMMENTS_COMPONENT, 403 ASSIGNFEEDBACK_COMMENTS_FILEAREA, 404 $grade->id 405 ); 406 407 $feedbackcomment = $this->get_feedback_comments($grade->id); 408 if ($feedbackcomment) { 409 $feedbackcomment->commenttext = $data->assignfeedbackcomments; 410 $feedbackcomment->commentformat = $data->assignfeedbackcommentsformat; 411 return $DB->update_record('assignfeedback_comments', $feedbackcomment); 412 } else { 413 $feedbackcomment = new stdClass(); 414 $feedbackcomment->commenttext = $data->assignfeedbackcomments; 415 $feedbackcomment->commentformat = $data->assignfeedbackcommentsformat; 416 $feedbackcomment->grade = $grade->id; 417 $feedbackcomment->assignment = $this->assignment->get_instance()->id; 418 return $DB->insert_record('assignfeedback_comments', $feedbackcomment) > 0; 419 } 420 } 421 422 /** 423 * Display the comment in the feedback table. 424 * 425 * @param stdClass $grade 426 * @param bool $showviewlink Set to true to show a link to view the full feedback 427 * @return string 428 */ 429 public function view_summary(stdClass $grade, & $showviewlink) { 430 $feedbackcomments = $this->get_feedback_comments($grade->id); 431 if ($feedbackcomments) { 432 $text = $this->rewrite_feedback_comments_urls($feedbackcomments->commenttext, $grade->id); 433 $text = format_text( 434 $text, 435 $feedbackcomments->commentformat, 436 [ 437 'context' => $this->assignment->get_context() 438 ] 439 ); 440 441 // Show the view all link if the text has been shortened. 442 $short = shorten_text($text, 140); 443 $showviewlink = $short != $text; 444 return $short; 445 } 446 return ''; 447 } 448 449 /** 450 * Display the comment in the feedback table. 451 * 452 * @param stdClass $grade 453 * @return string 454 */ 455 public function view(stdClass $grade) { 456 $feedbackcomments = $this->get_feedback_comments($grade->id); 457 if ($feedbackcomments) { 458 $text = $this->rewrite_feedback_comments_urls($feedbackcomments->commenttext, $grade->id); 459 $text = format_text( 460 $text, 461 $feedbackcomments->commentformat, 462 [ 463 'context' => $this->assignment->get_context() 464 ] 465 ); 466 467 return $text; 468 } 469 return ''; 470 } 471 472 /** 473 * Return true if this plugin can upgrade an old Moodle 2.2 assignment of this type 474 * and version. 475 * 476 * @param string $type old assignment subtype 477 * @param int $version old assignment version 478 * @return bool True if upgrade is possible 479 */ 480 public function can_upgrade($type, $version) { 481 482 if (($type == 'upload' || $type == 'uploadsingle' || 483 $type == 'online' || $type == 'offline') && $version >= 2011112900) { 484 return true; 485 } 486 return false; 487 } 488 489 /** 490 * Upgrade the settings from the old assignment to the new plugin based one 491 * 492 * @param context $oldcontext - the context for the old assignment 493 * @param stdClass $oldassignment - the data for the old assignment 494 * @param string $log - can be appended to by the upgrade 495 * @return bool was it a success? (false will trigger a rollback) 496 */ 497 public function upgrade_settings(context $oldcontext, stdClass $oldassignment, & $log) { 498 if ($oldassignment->assignmenttype == 'online') { 499 $this->set_config('commentinline', $oldassignment->var1); 500 return true; 501 } 502 return true; 503 } 504 505 /** 506 * Upgrade the feedback from the old assignment to the new one 507 * 508 * @param context $oldcontext - the database for the old assignment context 509 * @param stdClass $oldassignment The data record for the old assignment 510 * @param stdClass $oldsubmission The data record for the old submission 511 * @param stdClass $grade The data record for the new grade 512 * @param string $log Record upgrade messages in the log 513 * @return bool true or false - false will trigger a rollback 514 */ 515 public function upgrade(context $oldcontext, 516 stdClass $oldassignment, 517 stdClass $oldsubmission, 518 stdClass $grade, 519 & $log) { 520 global $DB; 521 522 $feedbackcomments = new stdClass(); 523 $feedbackcomments->commenttext = $oldsubmission->submissioncomment; 524 $feedbackcomments->commentformat = FORMAT_HTML; 525 526 $feedbackcomments->grade = $grade->id; 527 $feedbackcomments->assignment = $this->assignment->get_instance()->id; 528 if (!$DB->insert_record('assignfeedback_comments', $feedbackcomments) > 0) { 529 $log .= get_string('couldnotconvertgrade', 'mod_assign', $grade->userid); 530 return false; 531 } 532 533 return true; 534 } 535 536 /** 537 * If this plugin adds to the gradebook comments field, it must specify the format of the text 538 * of the comment 539 * 540 * Only one feedback plugin can push comments to the gradebook and that is chosen by the assignment 541 * settings page. 542 * 543 * @param stdClass $grade The grade 544 * @return int 545 */ 546 public function format_for_gradebook(stdClass $grade) { 547 $feedbackcomments = $this->get_feedback_comments($grade->id); 548 if ($feedbackcomments) { 549 return $feedbackcomments->commentformat; 550 } 551 return FORMAT_MOODLE; 552 } 553 554 /** 555 * If this plugin adds to the gradebook comments field, it must format the text 556 * of the comment 557 * 558 * Only one feedback plugin can push comments to the gradebook and that is chosen by the assignment 559 * settings page. 560 * 561 * @param stdClass $grade The grade 562 * @return string 563 */ 564 public function text_for_gradebook(stdClass $grade) { 565 $feedbackcomments = $this->get_feedback_comments($grade->id); 566 if ($feedbackcomments) { 567 return $feedbackcomments->commenttext; 568 } 569 return ''; 570 } 571 572 /** 573 * Return any files this plugin wishes to save to the gradebook. 574 * 575 * @param stdClass $grade The assign_grades object from the db 576 * @return array 577 */ 578 public function files_for_gradebook(stdClass $grade) : array { 579 return [ 580 'contextid' => $this->assignment->get_context()->id, 581 'component' => ASSIGNFEEDBACK_COMMENTS_COMPONENT, 582 'filearea' => ASSIGNFEEDBACK_COMMENTS_FILEAREA, 583 'itemid' => $grade->id 584 ]; 585 } 586 587 /** 588 * The assignment has been deleted - cleanup 589 * 590 * @return bool 591 */ 592 public function delete_instance() { 593 global $DB; 594 // Will throw exception on failure. 595 $DB->delete_records('assignfeedback_comments', 596 array('assignment'=>$this->assignment->get_instance()->id)); 597 return true; 598 } 599 600 /** 601 * Returns true if there are no feedback comments for the given grade. 602 * 603 * @param stdClass $grade 604 * @return bool 605 */ 606 public function is_empty(stdClass $grade) { 607 return $this->view($grade) == ''; 608 } 609 610 /** 611 * Get file areas returns a list of areas this plugin stores files 612 * @return array - An array of fileareas (keys) and descriptions (values) 613 */ 614 public function get_file_areas() { 615 return array(ASSIGNFEEDBACK_COMMENTS_FILEAREA => $this->get_name()); 616 } 617 618 /** 619 * Return a description of external params suitable for uploading an feedback comment from a webservice. 620 * 621 * @return \core_external\external_description|null 622 */ 623 public function get_external_parameters() { 624 $editorparams = array('text' => new external_value(PARAM_RAW, 'The text for this feedback.'), 625 'format' => new external_value(PARAM_INT, 'The format for this feedback')); 626 $editorstructure = new external_single_structure($editorparams, 'Editor structure', VALUE_OPTIONAL); 627 return array('assignfeedbackcomments_editor' => $editorstructure); 628 } 629 630 /** 631 * Return the plugin configs for external functions. 632 * 633 * @return array the list of settings 634 * @since Moodle 3.2 635 */ 636 public function get_config_for_external() { 637 return (array) $this->get_config(); 638 } 639 640 /** 641 * Convert encoded URLs in $text from the @@PLUGINFILE@@/... form to an actual URL. 642 * 643 * @param string $text the Text to check 644 * @param int $gradeid The grade ID which refers to the id in the gradebook 645 */ 646 private function rewrite_feedback_comments_urls(string $text, int $gradeid) { 647 return file_rewrite_pluginfile_urls( 648 $text, 649 'pluginfile.php', 650 $this->assignment->get_context()->id, 651 ASSIGNFEEDBACK_COMMENTS_COMPONENT, 652 ASSIGNFEEDBACK_COMMENTS_FILEAREA, 653 $gradeid 654 ); 655 } 656 657 /** 658 * File format options. 659 * 660 * @return array 661 */ 662 private function get_editor_options() { 663 global $COURSE; 664 665 return [ 666 'subdirs' => 1, 667 'maxbytes' => $COURSE->maxbytes, 668 'accepted_types' => '*', 669 'context' => $this->assignment->get_context(), 670 'maxfiles' => EDITOR_UNLIMITED_FILES 671 ]; 672 } 673 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body