Differences Between: [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 * This file contains a renderer for the assignment class 19 * 20 * @package mod_assign 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 namespace mod_assign\output; 26 27 use assign_files; 28 use html_writer; 29 use mod_assign\output\grading_app; 30 use portfolio_add_button; 31 use stored_file; 32 33 defined('MOODLE_INTERNAL') || die(); 34 35 require_once($CFG->dirroot . '/mod/assign/locallib.php'); 36 37 /** 38 * A custom renderer class that extends the plugin_renderer_base and is used by the assign module. 39 * 40 * @package mod_assign 41 * @copyright 2012 NetSpot {@link http://www.netspot.com.au} 42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 43 */ 44 class renderer extends \plugin_renderer_base { 45 46 /** @var string a unique ID. */ 47 public $htmlid; 48 49 /** 50 * Rendering assignment files 51 * 52 * @param \context $context 53 * @param int $userid 54 * @param string $filearea 55 * @param string $component 56 * @param \stdClass $course 57 * @param \stdClass $coursemodule 58 * @return string 59 */ 60 public function assign_files(\context $context, $userid, $filearea, $component, $course = null, $coursemodule = null) { 61 return $this->render(new \assign_files($context, $userid, $filearea, $component, $course, $coursemodule)); 62 } 63 64 /** 65 * Rendering assignment files 66 * 67 * @param \assign_files $tree 68 * @return string 69 */ 70 public function render_assign_files(\assign_files $tree) { 71 $this->htmlid = \html_writer::random_id('assign_files_tree'); 72 $this->page->requires->js_init_call('M.mod_assign.init_tree', array(true, $this->htmlid)); 73 $html = '<div id="'.$this->htmlid.'">'; 74 $html .= $this->htmllize_tree($tree, $tree->dir); 75 $html .= '</div>'; 76 77 if ($tree->portfolioform) { 78 $html .= $tree->portfolioform; 79 } 80 return $html; 81 } 82 83 /** 84 * Utility function to add a row of data to a table with 2 columns where the first column is the table's header. 85 * Modified the table param and does not return a value. 86 * 87 * @param \html_table $table The table to append the row of data to 88 * @param string $first The first column text 89 * @param string $second The second column text 90 * @param array $firstattributes The first column attributes (optional) 91 * @param array $secondattributes The second column attributes (optional) 92 * @return void 93 */ 94 private function add_table_row_tuple(\html_table $table, $first, $second, $firstattributes = [], 95 $secondattributes = []) { 96 $row = new \html_table_row(); 97 $cell1 = new \html_table_cell($first); 98 $cell1->header = true; 99 if (!empty($firstattributes)) { 100 $cell1->attributes = $firstattributes; 101 } 102 $cell2 = new \html_table_cell($second); 103 if (!empty($secondattributes)) { 104 $cell2->attributes = $secondattributes; 105 } 106 $row->cells = array($cell1, $cell2); 107 $table->data[] = $row; 108 } 109 110 /** 111 * Render a grading message notification 112 * @param \assign_gradingmessage $result The result to render 113 * @return string 114 */ 115 public function render_assign_gradingmessage(\assign_gradingmessage $result) { 116 $urlparams = array('id' => $result->coursemoduleid, 'action'=>'grading'); 117 if (!empty($result->page)) { 118 $urlparams['page'] = $result->page; 119 } 120 $url = new \moodle_url('/mod/assign/view.php', $urlparams); 121 $classes = $result->gradingerror ? 'notifyproblem' : 'notifysuccess'; 122 123 $o = ''; 124 $o .= $this->output->heading($result->heading, 4); 125 $o .= $this->output->notification($result->message, $classes); 126 $o .= $this->output->continue_button($url); 127 return $o; 128 } 129 130 /** 131 * Render the generic form 132 * @param \assign_form $form The form to render 133 * @return string 134 */ 135 public function render_assign_form(\assign_form $form) { 136 $o = ''; 137 if ($form->jsinitfunction) { 138 $this->page->requires->js_init_call($form->jsinitfunction, array()); 139 } 140 $o .= $this->output->box_start('boxaligncenter ' . $form->classname); 141 $o .= $this->moodleform($form->form); 142 $o .= $this->output->box_end(); 143 return $o; 144 } 145 146 /** 147 * Render the user summary 148 * 149 * @param \assign_user_summary $summary The user summary to render 150 * @return string 151 */ 152 public function render_assign_user_summary(\assign_user_summary $summary) { 153 $o = ''; 154 $supendedclass = ''; 155 $suspendedicon = ''; 156 157 if (!$summary->user) { 158 return; 159 } 160 161 if ($summary->suspendeduser) { 162 $supendedclass = ' usersuspended'; 163 $suspendedstring = get_string('userenrolmentsuspended', 'grades'); 164 $suspendedicon = ' ' . $this->pix_icon('i/enrolmentsuspended', $suspendedstring); 165 } 166 $o .= $this->output->container_start('usersummary'); 167 $o .= $this->output->box_start('boxaligncenter usersummarysection'.$supendedclass); 168 if ($summary->blindmarking) { 169 $o .= get_string('hiddenuser', 'assign') . $summary->uniqueidforuser.$suspendedicon; 170 } else { 171 $o .= $this->output->user_picture($summary->user); 172 $o .= $this->output->spacer(array('width'=>30)); 173 $urlparams = array('id' => $summary->user->id, 'course'=>$summary->courseid); 174 $url = new \moodle_url('/user/view.php', $urlparams); 175 $fullname = fullname($summary->user, $summary->viewfullnames); 176 $extrainfo = array(); 177 foreach ($summary->extrauserfields as $extrafield) { 178 $extrainfo[] = s($summary->user->$extrafield); 179 } 180 if (count($extrainfo)) { 181 $fullname .= ' (' . implode(', ', $extrainfo) . ')'; 182 } 183 $fullname .= $suspendedicon; 184 $o .= $this->output->action_link($url, $fullname); 185 } 186 $o .= $this->output->box_end(); 187 $o .= $this->output->container_end(); 188 189 return $o; 190 } 191 192 /** 193 * Render the submit for grading page 194 * 195 * @param \assign_submit_for_grading_page $page 196 * @return string 197 */ 198 public function render_assign_submit_for_grading_page($page) { 199 $o = ''; 200 201 $o .= $this->output->container_start('submitforgrading'); 202 $o .= $this->output->heading(get_string('confirmsubmissionheading', 'assign'), 3); 203 204 $cancelurl = new \moodle_url('/mod/assign/view.php', array('id' => $page->coursemoduleid)); 205 if (count($page->notifications)) { 206 // At least one of the submission plugins is not ready for submission. 207 208 $o .= $this->output->heading(get_string('submissionnotready', 'assign'), 4); 209 210 foreach ($page->notifications as $notification) { 211 $o .= $this->output->notification($notification); 212 } 213 214 $o .= $this->output->continue_button($cancelurl); 215 } else { 216 // All submission plugins ready - show the confirmation form. 217 $o .= $this->moodleform($page->confirmform); 218 } 219 $o .= $this->output->container_end(); 220 221 return $o; 222 } 223 224 /** 225 * Page is done - render the footer. 226 * 227 * @return void 228 */ 229 public function render_footer() { 230 return $this->output->footer(); 231 } 232 233 /** 234 * Render the header. 235 * 236 * @param assign_header $header 237 * @return string 238 */ 239 public function render_assign_header(assign_header $header) { 240 if ($header->subpage) { 241 $this->page->navbar->add($header->subpage, $header->subpageurl); 242 $args = ['contextname' => $header->context->get_context_name(false, true), 'subpage' => $header->subpage]; 243 $title = get_string('subpagetitle', 'assign', $args); 244 } else { 245 $title = $header->context->get_context_name(false, true); 246 } 247 $courseshortname = $header->context->get_course_context()->get_context_name(false, true); 248 $title = $courseshortname . ': ' . $title; 249 $heading = format_string($header->assign->name, false, array('context' => $header->context)); 250 251 $this->page->set_title($title); 252 $this->page->set_heading($this->page->course->fullname); 253 254 $description = $header->preface; 255 if ($header->showintro || $header->activity) { 256 $description = $this->output->box_start('generalbox boxaligncenter'); 257 if ($header->showintro) { 258 $description .= format_module_intro('assign', $header->assign, $header->coursemoduleid); 259 } 260 if ($header->activity) { 261 $description .= $this->format_activity_text($header->assign, $header->coursemoduleid); 262 } 263 $description .= $header->postfix; 264 $description .= $this->output->box_end(); 265 } 266 267 $activityheader = $this->page->activityheader; 268 $activityheader->set_attrs([ 269 'title' => $activityheader->is_title_allowed() ? $heading : '', 270 'description' => $description 271 ]); 272 273 return $this->output->header(); 274 } 275 276 /** 277 * Render the header for an individual plugin. 278 * 279 * @param \assign_plugin_header $header 280 * @return string 281 */ 282 public function render_assign_plugin_header(\assign_plugin_header $header) { 283 $o = $header->plugin->view_header(); 284 return $o; 285 } 286 287 /** 288 * Render a table containing the current status of the grading process. 289 * 290 * @param \assign_grading_summary $summary 291 * @return string 292 */ 293 public function render_assign_grading_summary(\assign_grading_summary $summary) { 294 // Create a table for the data. 295 $o = ''; 296 $o .= $this->output->container_start('gradingsummary'); 297 $o .= $this->output->heading(get_string('gradingsummary', 'assign'), 3); 298 299 if (isset($summary->cm)) { 300 $currenturl = new \moodle_url('/mod/assign/view.php', array('id' => $summary->cm->id)); 301 $o .= groups_print_activity_menu($summary->cm, $currenturl->out(), true); 302 } 303 304 $o .= $this->output->box_start('boxaligncenter gradingsummarytable'); 305 $t = new \html_table(); 306 $t->attributes['class'] = 'generaltable table-bordered'; 307 308 // Visibility Status. 309 $cell1content = get_string('hiddenfromstudents'); 310 $cell2content = (!$summary->isvisible) ? get_string('yes') : get_string('no'); 311 $this->add_table_row_tuple($t, $cell1content, $cell2content); 312 313 // Status. 314 if ($summary->teamsubmission) { 315 if ($summary->warnofungroupedusers === \assign_grading_summary::WARN_GROUPS_REQUIRED) { 316 $o .= $this->output->notification(get_string('ungroupedusers', 'assign')); 317 } else if ($summary->warnofungroupedusers === \assign_grading_summary::WARN_GROUPS_OPTIONAL) { 318 $o .= $this->output->notification(get_string('ungroupedusersoptional', 'assign')); 319 } 320 $cell1content = get_string('numberofteams', 'assign'); 321 } else { 322 $cell1content = get_string('numberofparticipants', 'assign'); 323 } 324 325 $cell2content = $summary->participantcount; 326 $this->add_table_row_tuple($t, $cell1content, $cell2content); 327 328 // Drafts count and dont show drafts count when using offline assignment. 329 if ($summary->submissiondraftsenabled && $summary->submissionsenabled) { 330 $cell1content = get_string('numberofdraftsubmissions', 'assign'); 331 $cell2content = $summary->submissiondraftscount; 332 $this->add_table_row_tuple($t, $cell1content, $cell2content); 333 } 334 335 // Submitted for grading. 336 if ($summary->submissionsenabled) { 337 $cell1content = get_string('numberofsubmittedassignments', 'assign'); 338 $cell2content = $summary->submissionssubmittedcount; 339 $this->add_table_row_tuple($t, $cell1content, $cell2content); 340 341 if (!$summary->teamsubmission) { 342 $cell1content = get_string('numberofsubmissionsneedgrading', 'assign'); 343 $cell2content = $summary->submissionsneedgradingcount; 344 $this->add_table_row_tuple($t, $cell1content, $cell2content); 345 } 346 } 347 348 $time = time(); 349 if ($summary->duedate) { 350 // Time remaining. 351 $duedate = $summary->duedate; 352 $cell1content = get_string('timeremaining', 'assign'); 353 if ($summary->courserelativedatesmode) { 354 $cell2content = get_string('relativedatessubmissiontimeleft', 'mod_assign'); 355 } else { 356 if ($duedate - $time <= 0) { 357 $cell2content = get_string('assignmentisdue', 'assign'); 358 } else { 359 $cell2content = format_time($duedate - $time); 360 } 361 } 362 363 $this->add_table_row_tuple($t, $cell1content, $cell2content); 364 365 if ($duedate < $time) { 366 $cell1content = get_string('latesubmissions', 'assign'); 367 $cutoffdate = $summary->cutoffdate; 368 if ($cutoffdate) { 369 if ($cutoffdate > $time) { 370 $cell2content = get_string('latesubmissionsaccepted', 'assign', userdate($summary->cutoffdate)); 371 } else { 372 $cell2content = get_string('nomoresubmissionsaccepted', 'assign'); 373 } 374 375 $this->add_table_row_tuple($t, $cell1content, $cell2content); 376 } 377 } 378 379 } 380 381 // Add time limit info if there is one. 382 $timelimitenabled = get_config('assign', 'enabletimelimit'); 383 if ($timelimitenabled && $summary->timelimit > 0) { 384 $cell1content = get_string('timelimit', 'assign'); 385 $cell2content = format_time($summary->timelimit); 386 $this->add_table_row_tuple($t, $cell1content, $cell2content, [], []); 387 } 388 389 // All done - write the table. 390 $o .= \html_writer::table($t); 391 $o .= $this->output->box_end(); 392 393 // Close the container and insert a spacer. 394 $o .= $this->output->container_end(); 395 $o .= \html_writer::end_tag('center'); 396 397 return $o; 398 } 399 400 /** 401 * Render a table containing all the current grades and feedback. 402 * 403 * @param \assign_feedback_status $status 404 * @return string 405 */ 406 public function render_assign_feedback_status(\assign_feedback_status $status) { 407 $o = ''; 408 409 $o .= $this->output->container_start('feedback'); 410 $o .= $this->output->heading(get_string('feedback', 'assign'), 3); 411 $o .= $this->output->box_start('boxaligncenter feedbacktable'); 412 $t = new \html_table(); 413 414 // Grade. 415 if (isset($status->gradefordisplay)) { 416 $cell1content = get_string('gradenoun'); 417 $cell2content = $status->gradefordisplay; 418 $this->add_table_row_tuple($t, $cell1content, $cell2content); 419 420 // Grade date. 421 $cell1content = get_string('gradedon', 'assign'); 422 $cell2content = userdate($status->gradeddate); 423 $this->add_table_row_tuple($t, $cell1content, $cell2content); 424 } 425 426 if ($status->grader) { 427 // Grader. 428 $cell1content = get_string('gradedby', 'assign'); 429 $cell2content = $this->output->user_picture($status->grader) . 430 $this->output->spacer(array('width' => 30)) . 431 fullname($status->grader, $status->canviewfullnames); 432 $this->add_table_row_tuple($t, $cell1content, $cell2content); 433 } 434 435 foreach ($status->feedbackplugins as $plugin) { 436 if ($plugin->is_enabled() && 437 $plugin->is_visible() && 438 $plugin->has_user_summary() && 439 !empty($status->grade) && 440 !$plugin->is_empty($status->grade)) { 441 442 $displaymode = \assign_feedback_plugin_feedback::SUMMARY; 443 $pluginfeedback = new \assign_feedback_plugin_feedback($plugin, 444 $status->grade, 445 $displaymode, 446 $status->coursemoduleid, 447 $status->returnaction, 448 $status->returnparams); 449 $cell1content = $plugin->get_name(); 450 $cell2content = $this->render($pluginfeedback); 451 $this->add_table_row_tuple($t, $cell1content, $cell2content); 452 } 453 } 454 455 $o .= \html_writer::table($t); 456 $o .= $this->output->box_end(); 457 458 if (!empty($status->gradingcontrollergrade)) { 459 $o .= $this->output->heading(get_string('gradebreakdown', 'assign'), 4); 460 $o .= $status->gradingcontrollergrade; 461 } 462 463 $o .= $this->output->container_end(); 464 return $o; 465 } 466 467 /** 468 * Render a compact view of the current status of the submission. 469 * 470 * @param \assign_submission_status_compact $status 471 * @return string 472 */ 473 public function render_assign_submission_status_compact(\assign_submission_status_compact $status) { 474 $o = ''; 475 $o .= $this->output->container_start('submissionstatustable'); 476 $o .= $this->output->heading(get_string('submission', 'assign'), 3); 477 478 if ($status->teamsubmissionenabled) { 479 $group = $status->submissiongroup; 480 if ($group) { 481 $team = format_string($group->name, false, $status->context); 482 } else if ($status->preventsubmissionnotingroup) { 483 if (count($status->usergroups) == 0) { 484 $team = '<span class="alert alert-error">' . get_string('noteam', 'assign') . '</span>'; 485 } else if (count($status->usergroups) > 1) { 486 $team = '<span class="alert alert-error">' . get_string('multipleteams', 'assign') . '</span>'; 487 } 488 } else { 489 $team = get_string('defaultteam', 'assign'); 490 } 491 $o .= $this->output->container(get_string('teamname', 'assign', $team), 'teamname'); 492 } 493 494 if (!$status->teamsubmissionenabled) { 495 if ($status->submission && $status->submission->status != ASSIGN_SUBMISSION_STATUS_NEW) { 496 $statusstr = get_string('submissionstatus_' . $status->submission->status, 'assign'); 497 $o .= $this->output->container($statusstr, 'submissionstatus' . $status->submission->status); 498 } else { 499 if (!$status->submissionsenabled) { 500 $o .= $this->output->container(get_string('noonlinesubmissions', 'assign'), 'submissionstatus'); 501 } else { 502 $o .= $this->output->container(get_string('noattempt', 'assign'), 'submissionstatus'); 503 } 504 } 505 } else { 506 $group = $status->submissiongroup; 507 if (!$group && $status->preventsubmissionnotingroup) { 508 $o .= $this->output->container(get_string('nosubmission', 'assign'), 'submissionstatus'); 509 } else if ($status->teamsubmission && $status->teamsubmission->status != ASSIGN_SUBMISSION_STATUS_NEW) { 510 $teamstatus = $status->teamsubmission->status; 511 $submissionsummary = get_string('submissionstatus_' . $teamstatus, 'assign'); 512 $groupid = 0; 513 if ($status->submissiongroup) { 514 $groupid = $status->submissiongroup->id; 515 } 516 517 $members = $status->submissiongroupmemberswhoneedtosubmit; 518 $userslist = array(); 519 foreach ($members as $member) { 520 $urlparams = array('id' => $member->id, 'course' => $status->courseid); 521 $url = new \moodle_url('/user/view.php', $urlparams); 522 if ($status->view == assign_submission_status::GRADER_VIEW && $status->blindmarking) { 523 $userslist[] = $member->alias; 524 } else { 525 $fullname = fullname($member, $status->canviewfullnames); 526 $userslist[] = $this->output->action_link($url, $fullname); 527 } 528 } 529 if (count($userslist) > 0) { 530 $userstr = join(', ', $userslist); 531 $formatteduserstr = get_string('userswhoneedtosubmit', 'assign', $userstr); 532 $submissionsummary .= $this->output->container($formatteduserstr); 533 } 534 $o .= $this->output->container($submissionsummary, 'submissionstatus' . $status->teamsubmission->status); 535 } else { 536 if (!$status->submissionsenabled) { 537 $o .= $this->output->container(get_string('noonlinesubmissions', 'assign'), 'submissionstatus'); 538 } else { 539 $o .= $this->output->container(get_string('nosubmission', 'assign'), 'submissionstatus'); 540 } 541 } 542 } 543 544 // Is locked? 545 if ($status->locked) { 546 $o .= $this->output->container(get_string('submissionslocked', 'assign'), 'submissionlocked'); 547 } 548 549 // Grading status. 550 $statusstr = ''; 551 $classname = 'gradingstatus'; 552 if ($status->gradingstatus == ASSIGN_GRADING_STATUS_GRADED || 553 $status->gradingstatus == ASSIGN_GRADING_STATUS_NOT_GRADED) { 554 $statusstr = get_string($status->gradingstatus, 'assign'); 555 } else { 556 $gradingstatus = 'markingworkflowstate' . $status->gradingstatus; 557 $statusstr = get_string($gradingstatus, 'assign'); 558 } 559 if ($status->gradingstatus == ASSIGN_GRADING_STATUS_GRADED || 560 $status->gradingstatus == ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) { 561 $classname = 'submissiongraded'; 562 } else { 563 $classname = 'submissionnotgraded'; 564 } 565 566 $o .= $this->output->container($statusstr, $classname); 567 568 $submission = $status->teamsubmission ? $status->teamsubmission : $status->submission; 569 $duedate = $status->duedate; 570 if ($duedate > 0) { 571 572 if ($status->extensionduedate) { 573 // Extension date. 574 $duedate = $status->extensionduedate; 575 } 576 } 577 578 // Time remaining. 579 // Only add the row if there is a due date, or a countdown. 580 if ($status->duedate > 0 || !empty($submission->timestarted)) { 581 [$remaining, $classname] = $this->get_time_remaining($status); 582 583 // If the assignment is not submitted, and there is a submission in progress, 584 // Add a heading for the time limit. 585 if (!empty($submission) && 586 $submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED && 587 !empty($submission->timestarted) 588 ) { 589 $o .= $this->output->container(get_string('timeremaining', 'assign')); 590 } 591 $o .= $this->output->container($remaining, $classname); 592 } 593 594 // Show graders whether this submission is editable by students. 595 if ($status->view == assign_submission_status::GRADER_VIEW) { 596 if ($status->canedit) { 597 $o .= $this->output->container(get_string('submissioneditable', 'assign'), 'submissioneditable'); 598 } else { 599 $o .= $this->output->container(get_string('submissionnoteditable', 'assign'), 'submissionnoteditable'); 600 } 601 } 602 603 // Grading criteria preview. 604 if (!empty($status->gradingcontrollerpreview)) { 605 $o .= $this->output->container($status->gradingcontrollerpreview, 'gradingmethodpreview'); 606 } 607 608 if ($submission) { 609 610 if (!$status->teamsubmission || $status->submissiongroup != false || !$status->preventsubmissionnotingroup) { 611 foreach ($status->submissionplugins as $plugin) { 612 $pluginshowsummary = !$plugin->is_empty($submission) || !$plugin->allow_submissions(); 613 if ($plugin->is_enabled() && 614 $plugin->is_visible() && 615 $plugin->has_user_summary() && 616 $pluginshowsummary 617 ) { 618 619 $displaymode = \assign_submission_plugin_submission::SUMMARY; 620 $pluginsubmission = new \assign_submission_plugin_submission($plugin, 621 $submission, 622 $displaymode, 623 $status->coursemoduleid, 624 $status->returnaction, 625 $status->returnparams); 626 $plugincomponent = $plugin->get_subtype() . '_' . $plugin->get_type(); 627 $o .= $this->output->container($this->render($pluginsubmission), 'assignsubmission ' . $plugincomponent); 628 } 629 } 630 } 631 } 632 633 $o .= $this->output->container_end(); 634 return $o; 635 } 636 637 /** 638 * Render a table containing the current status of the submission. 639 * 640 * @param assign_submission_status $status 641 * @return string 642 */ 643 public function render_assign_submission_status(assign_submission_status $status) { 644 $o = ''; 645 $o .= $this->output->container_start('submissionstatustable'); 646 $o .= $this->output->heading(get_string('submissionstatusheading', 'assign'), 3); 647 $time = time(); 648 649 $o .= $this->output->box_start('boxaligncenter submissionsummarytable'); 650 651 $t = new \html_table(); 652 $t->attributes['class'] = 'generaltable table-bordered'; 653 654 $warningmsg = ''; 655 if ($status->teamsubmissionenabled) { 656 $cell1content = get_string('submissionteam', 'assign'); 657 $group = $status->submissiongroup; 658 if ($group) { 659 $cell2content = format_string($group->name, false, ['context' => $status->context]); 660 } else if ($status->preventsubmissionnotingroup) { 661 if (count($status->usergroups) == 0) { 662 $notification = new \core\output\notification(get_string('noteam', 'assign'), 'error'); 663 $notification->set_show_closebutton(false); 664 $warningmsg = $this->output->notification(get_string('noteam_desc', 'assign'), 'error'); 665 } else if (count($status->usergroups) > 1) { 666 $notification = new \core\output\notification(get_string('multipleteams', 'assign'), 'error'); 667 $notification->set_show_closebutton(false); 668 $warningmsg = $this->output->notification(get_string('multipleteams_desc', 'assign'), 'error'); 669 } 670 $cell2content = $this->output->render($notification); 671 } else { 672 $cell2content = get_string('defaultteam', 'assign'); 673 } 674 675 $this->add_table_row_tuple($t, $cell1content, $cell2content); 676 } 677 678 if ($status->attemptreopenmethod != ASSIGN_ATTEMPT_REOPEN_METHOD_NONE) { 679 $currentattempt = 1; 680 if (!$status->teamsubmissionenabled) { 681 if ($status->submission) { 682 $currentattempt = $status->submission->attemptnumber + 1; 683 } 684 } else { 685 if ($status->teamsubmission) { 686 $currentattempt = $status->teamsubmission->attemptnumber + 1; 687 } 688 } 689 690 $cell1content = get_string('attemptnumber', 'assign'); 691 $maxattempts = $status->maxattempts; 692 if ($maxattempts == ASSIGN_UNLIMITED_ATTEMPTS) { 693 $cell2content = get_string('currentattempt', 'assign', $currentattempt); 694 } else { 695 $cell2content = get_string('currentattemptof', 'assign', 696 array('attemptnumber' => $currentattempt, 'maxattempts' => $maxattempts)); 697 } 698 699 $this->add_table_row_tuple($t, $cell1content, $cell2content); 700 } 701 702 $cell1content = get_string('submissionstatus', 'assign'); 703 $cell2attributes = []; 704 if (!$status->teamsubmissionenabled) { 705 if ($status->submission && $status->submission->status != ASSIGN_SUBMISSION_STATUS_NEW) { 706 $cell2content = get_string('submissionstatus_' . $status->submission->status, 'assign'); 707 $cell2attributes = array('class' => 'submissionstatus' . $status->submission->status); 708 } else { 709 if (!$status->submissionsenabled) { 710 $cell2content = get_string('noonlinesubmissions', 'assign'); 711 } else { 712 $cell2content = get_string('nosubmissionyet', 'assign'); 713 } 714 } 715 } else { 716 $group = $status->submissiongroup; 717 if (!$group && $status->preventsubmissionnotingroup) { 718 $cell2content = get_string('nosubmission', 'assign'); 719 } else if ($status->teamsubmission && $status->teamsubmission->status != ASSIGN_SUBMISSION_STATUS_NEW) { 720 $teamstatus = $status->teamsubmission->status; 721 $cell2content = get_string('submissionstatus_' . $teamstatus, 'assign'); 722 723 $members = $status->submissiongroupmemberswhoneedtosubmit; 724 $userslist = array(); 725 foreach ($members as $member) { 726 $urlparams = array('id' => $member->id, 'course'=>$status->courseid); 727 $url = new \moodle_url('/user/view.php', $urlparams); 728 if ($status->view == assign_submission_status::GRADER_VIEW && $status->blindmarking) { 729 $userslist[] = $member->alias; 730 } else { 731 $fullname = fullname($member, $status->canviewfullnames); 732 $userslist[] = $this->output->action_link($url, $fullname); 733 } 734 } 735 if (count($userslist) > 0) { 736 $userstr = join(', ', $userslist); 737 $formatteduserstr = get_string('userswhoneedtosubmit', 'assign', $userstr); 738 $cell2content .= $this->output->container($formatteduserstr); 739 } 740 741 $cell2attributes = array('class' => 'submissionstatus' . $status->teamsubmission->status); 742 } else { 743 if (!$status->submissionsenabled) { 744 $cell2content = get_string('noonlinesubmissions', 'assign'); 745 } else { 746 $cell2content = get_string('nosubmission', 'assign'); 747 } 748 } 749 } 750 751 $this->add_table_row_tuple($t, $cell1content, $cell2content, [], $cell2attributes); 752 753 // Is locked? 754 if ($status->locked) { 755 $cell1content = ''; 756 $cell2content = get_string('submissionslocked', 'assign'); 757 $cell2attributes = array('class' => 'submissionlocked'); 758 $this->add_table_row_tuple($t, $cell1content, $cell2content, [], $cell2attributes); 759 } 760 761 // Grading status. 762 $cell1content = get_string('gradingstatus', 'assign'); 763 if ($status->gradingstatus == ASSIGN_GRADING_STATUS_GRADED || 764 $status->gradingstatus == ASSIGN_GRADING_STATUS_NOT_GRADED) { 765 $cell2content = get_string($status->gradingstatus, 'assign'); 766 } else { 767 $gradingstatus = 'markingworkflowstate' . $status->gradingstatus; 768 $cell2content = get_string($gradingstatus, 'assign'); 769 } 770 if ($status->gradingstatus == ASSIGN_GRADING_STATUS_GRADED || 771 $status->gradingstatus == ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) { 772 $cell2attributes = array('class' => 'submissiongraded'); 773 } else { 774 $cell2attributes = array('class' => 'submissionnotgraded'); 775 } 776 $this->add_table_row_tuple($t, $cell1content, $cell2content, [], $cell2attributes); 777 778 $submission = $status->teamsubmission ? $status->teamsubmission : $status->submission; 779 $duedate = $status->duedate; 780 if ($duedate > 0) { 781 if ($status->view == assign_submission_status::GRADER_VIEW) { 782 if ($status->cutoffdate) { 783 // Cut off date. 784 $cell1content = get_string('cutoffdate', 'assign'); 785 $cell2content = userdate($status->cutoffdate); 786 $this->add_table_row_tuple($t, $cell1content, $cell2content); 787 } 788 } 789 790 if ($status->extensionduedate) { 791 // Extension date. 792 $cell1content = get_string('extensionduedate', 'assign'); 793 $cell2content = userdate($status->extensionduedate); 794 $this->add_table_row_tuple($t, $cell1content, $cell2content); 795 $duedate = $status->extensionduedate; 796 } 797 } 798 799 // Time remaining. 800 // Only add the row if there is a due date, or a countdown. 801 if ($status->duedate > 0 || !empty($submission->timestarted)) { 802 $cell1content = get_string('timeremaining', 'assign'); 803 [$cell2content, $cell2attributes] = $this->get_time_remaining($status); 804 $this->add_table_row_tuple($t, $cell1content, $cell2content, [], ['class' => $cell2attributes]); 805 } 806 807 // Add time limit info if there is one. 808 $timelimitenabled = get_config('assign', 'enabletimelimit') && $status->timelimit > 0; 809 if ($timelimitenabled && $status->timelimit > 0) { 810 $cell1content = get_string('timelimit', 'assign'); 811 $cell2content = format_time($status->timelimit); 812 $this->add_table_row_tuple($t, $cell1content, $cell2content, [], []); 813 } 814 815 // Show graders whether this submission is editable by students. 816 if ($status->view == assign_submission_status::GRADER_VIEW) { 817 $cell1content = get_string('editingstatus', 'assign'); 818 if ($status->canedit) { 819 $cell2content = get_string('submissioneditable', 'assign'); 820 $cell2attributes = array('class' => 'submissioneditable'); 821 } else { 822 $cell2content = get_string('submissionnoteditable', 'assign'); 823 $cell2attributes = array('class' => 'submissionnoteditable'); 824 } 825 $this->add_table_row_tuple($t, $cell1content, $cell2content, [], $cell2attributes); 826 } 827 828 // Last modified. 829 if ($submission) { 830 $cell1content = get_string('timemodified', 'assign'); 831 832 if ($submission->status != ASSIGN_SUBMISSION_STATUS_NEW) { 833 $cell2content = userdate($submission->timemodified); 834 } else { 835 $cell2content = "-"; 836 } 837 838 $this->add_table_row_tuple($t, $cell1content, $cell2content); 839 840 if (!$status->teamsubmission || $status->submissiongroup != false || !$status->preventsubmissionnotingroup) { 841 foreach ($status->submissionplugins as $plugin) { 842 $pluginshowsummary = !$plugin->is_empty($submission) || !$plugin->allow_submissions(); 843 if ($plugin->is_enabled() && 844 $plugin->is_visible() && 845 $plugin->has_user_summary() && 846 $pluginshowsummary 847 ) { 848 849 $cell1content = $plugin->get_name(); 850 $displaymode = \assign_submission_plugin_submission::SUMMARY; 851 $pluginsubmission = new \assign_submission_plugin_submission($plugin, 852 $submission, 853 $displaymode, 854 $status->coursemoduleid, 855 $status->returnaction, 856 $status->returnparams); 857 $cell2content = $this->render($pluginsubmission); 858 $this->add_table_row_tuple($t, $cell1content, $cell2content); 859 } 860 } 861 } 862 } 863 864 $o .= $warningmsg; 865 $o .= \html_writer::table($t); 866 $o .= $this->output->box_end(); 867 868 // Grading criteria preview. 869 if (!empty($status->gradingcontrollerpreview)) { 870 $o .= $this->output->heading(get_string('gradingmethodpreview', 'assign'), 4); 871 $o .= $status->gradingcontrollerpreview; 872 } 873 874 $o .= $this->output->container_end(); 875 return $o; 876 } 877 878 /** 879 * Output the attempt history chooser for this assignment 880 * 881 * @param \assign_attempt_history_chooser $history 882 * @return string 883 */ 884 public function render_assign_attempt_history_chooser(\assign_attempt_history_chooser $history) { 885 $o = ''; 886 887 $context = $history->export_for_template($this); 888 $o .= $this->render_from_template('mod_assign/attempt_history_chooser', $context); 889 890 return $o; 891 } 892 893 /** 894 * Output the attempt history for this assignment 895 * 896 * @param \assign_attempt_history $history 897 * @return string 898 */ 899 public function render_assign_attempt_history(\assign_attempt_history $history) { 900 $o = ''; 901 902 // Don't show the last one because it is the current submission. 903 array_pop($history->submissions); 904 905 // Show newest to oldest. 906 $history->submissions = array_reverse($history->submissions); 907 908 if (empty($history->submissions)) { 909 return ''; 910 } 911 912 $containerid = 'attempthistory' . uniqid(); 913 $o .= $this->output->heading(get_string('attempthistory', 'assign'), 3); 914 $o .= $this->box_start('attempthistory', $containerid); 915 916 foreach ($history->submissions as $i => $submission) { 917 $grade = null; 918 foreach ($history->grades as $onegrade) { 919 if ($onegrade->attemptnumber == $submission->attemptnumber) { 920 if ($onegrade->grade != ASSIGN_GRADE_NOT_SET) { 921 $grade = $onegrade; 922 } 923 break; 924 } 925 } 926 927 if ($submission) { 928 $submissionsummary = userdate($submission->timemodified); 929 } else { 930 $submissionsummary = get_string('nosubmission', 'assign'); 931 } 932 933 $attemptsummaryparams = array('attemptnumber'=>$submission->attemptnumber+1, 934 'submissionsummary'=>$submissionsummary); 935 $o .= $this->heading(get_string('attemptheading', 'assign', $attemptsummaryparams), 4); 936 937 $t = new \html_table(); 938 939 if ($submission) { 940 $cell1content = get_string('submissionstatus', 'assign'); 941 $cell2content = get_string('submissionstatus_' . $submission->status, 'assign'); 942 $this->add_table_row_tuple($t, $cell1content, $cell2content); 943 944 foreach ($history->submissionplugins as $plugin) { 945 $pluginshowsummary = !$plugin->is_empty($submission) || !$plugin->allow_submissions(); 946 if ($plugin->is_enabled() && 947 $plugin->is_visible() && 948 $plugin->has_user_summary() && 949 $pluginshowsummary) { 950 951 $cell1content = $plugin->get_name(); 952 $pluginsubmission = new \assign_submission_plugin_submission($plugin, 953 $submission, 954 \assign_submission_plugin_submission::SUMMARY, 955 $history->coursemoduleid, 956 $history->returnaction, 957 $history->returnparams); 958 $cell2content = $this->render($pluginsubmission); 959 $this->add_table_row_tuple($t, $cell1content, $cell2content); 960 } 961 } 962 } 963 964 if ($grade) { 965 // Heading 'feedback'. 966 $title = get_string('feedback', 'assign', $i); 967 $title .= $this->output->spacer(array('width'=>10)); 968 if ($history->cangrade) { 969 // Edit previous feedback. 970 $returnparams = http_build_query($history->returnparams); 971 $urlparams = array('id' => $history->coursemoduleid, 972 'rownum'=>$history->rownum, 973 'useridlistid'=>$history->useridlistid, 974 'attemptnumber'=>$grade->attemptnumber, 975 'action'=>'grade', 976 'returnaction'=>$history->returnaction, 977 'returnparams'=>$returnparams); 978 $url = new \moodle_url('/mod/assign/view.php', $urlparams); 979 $icon = new \pix_icon('gradefeedback', 980 get_string('editattemptfeedback', 'assign', $grade->attemptnumber+1), 981 'mod_assign'); 982 $title .= $this->output->action_icon($url, $icon); 983 } 984 $cell = new \html_table_cell($title); 985 $cell->attributes['class'] = 'feedbacktitle'; 986 $cell->colspan = 2; 987 $t->data[] = new \html_table_row(array($cell)); 988 989 // Grade. 990 $cell1content = get_string('gradenoun'); 991 $cell2content = $grade->gradefordisplay; 992 $this->add_table_row_tuple($t, $cell1content, $cell2content); 993 994 // Graded on. 995 $cell1content = get_string('gradedon', 'assign'); 996 $cell2content = userdate($grade->timemodified); 997 $this->add_table_row_tuple($t, $cell1content, $cell2content); 998 999 // Graded by set to a real user. Not set can be empty or -1. 1000 if (!empty($grade->grader) && is_object($grade->grader)) { 1001 $cell1content = get_string('gradedby', 'assign'); 1002 $cell2content = $this->output->user_picture($grade->grader) . 1003 $this->output->spacer(array('width' => 30)) . fullname($grade->grader); 1004 $this->add_table_row_tuple($t, $cell1content, $cell2content); 1005 } 1006 1007 // Feedback from plugins. 1008 foreach ($history->feedbackplugins as $plugin) { 1009 if ($plugin->is_enabled() && 1010 $plugin->is_visible() && 1011 $plugin->has_user_summary() && 1012 !$plugin->is_empty($grade)) { 1013 1014 $pluginfeedback = new \assign_feedback_plugin_feedback( 1015 $plugin, $grade, \assign_feedback_plugin_feedback::SUMMARY, $history->coursemoduleid, 1016 $history->returnaction, $history->returnparams 1017 ); 1018 1019 $cell1content = $plugin->get_name(); 1020 $cell2content = $this->render($pluginfeedback); 1021 $this->add_table_row_tuple($t, $cell1content, $cell2content); 1022 } 1023 1024 } 1025 1026 } 1027 1028 $o .= \html_writer::table($t); 1029 } 1030 $o .= $this->box_end(); 1031 1032 $this->page->requires->yui_module('moodle-mod_assign-history', 'Y.one("#' . $containerid . '").history'); 1033 1034 return $o; 1035 } 1036 1037 /** 1038 * Render a submission plugin submission 1039 * 1040 * @param \assign_submission_plugin_submission $submissionplugin 1041 * @return string 1042 */ 1043 public function render_assign_submission_plugin_submission(\assign_submission_plugin_submission $submissionplugin) { 1044 $o = ''; 1045 1046 if ($submissionplugin->view == \assign_submission_plugin_submission::SUMMARY) { 1047 $showviewlink = false; 1048 $summary = $submissionplugin->plugin->view_summary($submissionplugin->submission, 1049 $showviewlink); 1050 1051 $classsuffix = $submissionplugin->plugin->get_subtype() . 1052 '_' . 1053 $submissionplugin->plugin->get_type() . 1054 '_' . 1055 $submissionplugin->submission->id; 1056 1057 $o .= $this->output->box_start('boxaligncenter plugincontentsummary summary_' . $classsuffix); 1058 1059 $link = ''; 1060 if ($showviewlink) { 1061 $previewstr = get_string('viewsubmission', 'assign'); 1062 $icon = $this->output->pix_icon('t/preview', $previewstr); 1063 1064 $expandstr = get_string('viewfull', 'assign'); 1065 $expandicon = $this->output->pix_icon('t/switch_plus', $expandstr); 1066 $options = array( 1067 'class' => 'expandsummaryicon expand_' . $classsuffix, 1068 'aria-label' => $expandstr, 1069 'role' => 'button', 1070 'aria-expanded' => 'false' 1071 ); 1072 $o .= \html_writer::link('', $expandicon, $options); 1073 1074 $jsparams = array($submissionplugin->plugin->get_subtype(), 1075 $submissionplugin->plugin->get_type(), 1076 $submissionplugin->submission->id); 1077 1078 $this->page->requires->js_init_call('M.mod_assign.init_plugin_summary', $jsparams); 1079 1080 $action = 'viewplugin' . $submissionplugin->plugin->get_subtype(); 1081 $returnparams = http_build_query($submissionplugin->returnparams); 1082 $link .= '<noscript>'; 1083 $urlparams = array('id' => $submissionplugin->coursemoduleid, 1084 'sid'=>$submissionplugin->submission->id, 1085 'plugin'=>$submissionplugin->plugin->get_type(), 1086 'action'=>$action, 1087 'returnaction'=>$submissionplugin->returnaction, 1088 'returnparams'=>$returnparams); 1089 $url = new \moodle_url('/mod/assign/view.php', $urlparams); 1090 $link .= $this->output->action_link($url, $icon); 1091 $link .= '</noscript>'; 1092 1093 $link .= $this->output->spacer(array('width'=>15)); 1094 } 1095 1096 $o .= $link . $summary; 1097 $o .= $this->output->box_end(); 1098 if ($showviewlink) { 1099 $o .= $this->output->box_start('boxaligncenter hidefull full_' . $classsuffix); 1100 $collapsestr = get_string('viewsummary', 'assign'); 1101 $options = array( 1102 'class' => 'expandsummaryicon contract_' . $classsuffix, 1103 'aria-label' => $collapsestr, 1104 'role' => 'button', 1105 'aria-expanded' => 'true' 1106 ); 1107 $collapseicon = $this->output->pix_icon('t/switch_minus', $collapsestr); 1108 $o .= \html_writer::link('', $collapseicon, $options); 1109 1110 $o .= $submissionplugin->plugin->view($submissionplugin->submission); 1111 $o .= $this->output->box_end(); 1112 } 1113 } else if ($submissionplugin->view == \assign_submission_plugin_submission::FULL) { 1114 $o .= $this->output->box_start('boxaligncenter submissionfull'); 1115 $o .= $submissionplugin->plugin->view($submissionplugin->submission); 1116 $o .= $this->output->box_end(); 1117 } 1118 1119 return $o; 1120 } 1121 1122 /** 1123 * Render the grading table. 1124 * 1125 * @param \assign_grading_table $table 1126 * @return string 1127 */ 1128 public function render_assign_grading_table(\assign_grading_table $table) { 1129 $o = ''; 1130 $o .= $this->output->box_start('boxaligncenter gradingtable position-relative'); 1131 1132 $this->page->requires->js_init_call('M.mod_assign.init_grading_table', array()); 1133 $this->page->requires->string_for_js('nousersselected', 'assign'); 1134 $this->page->requires->string_for_js('batchoperationconfirmgrantextension', 'assign'); 1135 $this->page->requires->string_for_js('batchoperationconfirmlock', 'assign'); 1136 $this->page->requires->string_for_js('batchoperationconfirmremovesubmission', 'assign'); 1137 $this->page->requires->string_for_js('batchoperationconfirmreverttodraft', 'assign'); 1138 $this->page->requires->string_for_js('batchoperationconfirmunlock', 'assign'); 1139 $this->page->requires->string_for_js('batchoperationconfirmaddattempt', 'assign'); 1140 $this->page->requires->string_for_js('batchoperationconfirmdownloadselected', 'assign'); 1141 $this->page->requires->string_for_js('batchoperationconfirmsetmarkingworkflowstate', 'assign'); 1142 $this->page->requires->string_for_js('batchoperationconfirmsetmarkingallocation', 'assign'); 1143 $this->page->requires->string_for_js('editaction', 'assign'); 1144 foreach ($table->plugingradingbatchoperations as $plugin => $operations) { 1145 foreach ($operations as $operation => $description) { 1146 $this->page->requires->string_for_js('batchoperationconfirm' . $operation, 1147 'assignfeedback_' . $plugin); 1148 } 1149 } 1150 $o .= $this->flexible_table($table, $table->get_rows_per_page(), true); 1151 $o .= $this->output->box_end(); 1152 1153 return $o; 1154 } 1155 1156 /** 1157 * Render a feedback plugin feedback 1158 * 1159 * @param \assign_feedback_plugin_feedback $feedbackplugin 1160 * @return string 1161 */ 1162 public function render_assign_feedback_plugin_feedback(\assign_feedback_plugin_feedback $feedbackplugin) { 1163 $o = ''; 1164 1165 if ($feedbackplugin->view == \assign_feedback_plugin_feedback::SUMMARY) { 1166 $showviewlink = false; 1167 $summary = $feedbackplugin->plugin->view_summary($feedbackplugin->grade, $showviewlink); 1168 1169 $classsuffix = $feedbackplugin->plugin->get_subtype() . 1170 '_' . 1171 $feedbackplugin->plugin->get_type() . 1172 '_' . 1173 $feedbackplugin->grade->id; 1174 $o .= $this->output->box_start('boxaligncenter plugincontentsummary summary_' . $classsuffix); 1175 1176 $link = ''; 1177 if ($showviewlink) { 1178 $previewstr = get_string('viewfeedback', 'assign'); 1179 $icon = $this->output->pix_icon('t/preview', $previewstr); 1180 1181 $expandstr = get_string('viewfull', 'assign'); 1182 $expandicon = $this->output->pix_icon('t/switch_plus', $expandstr); 1183 $options = array( 1184 'class' => 'expandsummaryicon expand_' . $classsuffix, 1185 'aria-label' => $expandstr, 1186 'role' => 'button', 1187 'aria-expanded' => 'false' 1188 ); 1189 $o .= \html_writer::link('', $expandicon, $options); 1190 1191 $jsparams = array($feedbackplugin->plugin->get_subtype(), 1192 $feedbackplugin->plugin->get_type(), 1193 $feedbackplugin->grade->id); 1194 $this->page->requires->js_init_call('M.mod_assign.init_plugin_summary', $jsparams); 1195 1196 $urlparams = array('id' => $feedbackplugin->coursemoduleid, 1197 'gid'=>$feedbackplugin->grade->id, 1198 'plugin'=>$feedbackplugin->plugin->get_type(), 1199 'action'=>'viewplugin' . $feedbackplugin->plugin->get_subtype(), 1200 'returnaction'=>$feedbackplugin->returnaction, 1201 'returnparams'=>http_build_query($feedbackplugin->returnparams)); 1202 $url = new \moodle_url('/mod/assign/view.php', $urlparams); 1203 $link .= '<noscript>'; 1204 $link .= $this->output->action_link($url, $icon); 1205 $link .= '</noscript>'; 1206 1207 $link .= $this->output->spacer(array('width'=>15)); 1208 } 1209 1210 $o .= $link . $summary; 1211 $o .= $this->output->box_end(); 1212 if ($showviewlink) { 1213 $o .= $this->output->box_start('boxaligncenter hidefull full_' . $classsuffix); 1214 $collapsestr = get_string('viewsummary', 'assign'); 1215 $options = array( 1216 'class' => 'expandsummaryicon contract_' . $classsuffix, 1217 'aria-label' => $collapsestr, 1218 'role' => 'button', 1219 'aria-expanded' => 'true' 1220 ); 1221 $collapseicon = $this->output->pix_icon('t/switch_minus', $collapsestr); 1222 $o .= \html_writer::link('', $collapseicon, $options); 1223 1224 $o .= $feedbackplugin->plugin->view($feedbackplugin->grade); 1225 $o .= $this->output->box_end(); 1226 } 1227 } else if ($feedbackplugin->view == \assign_feedback_plugin_feedback::FULL) { 1228 $o .= $this->output->box_start('boxaligncenter feedbackfull'); 1229 $o .= $feedbackplugin->plugin->view($feedbackplugin->grade); 1230 $o .= $this->output->box_end(); 1231 } 1232 1233 return $o; 1234 } 1235 1236 /** 1237 * Render a course index summary 1238 * 1239 * @param \assign_course_index_summary $indexsummary 1240 * @return string 1241 */ 1242 public function render_assign_course_index_summary(\assign_course_index_summary $indexsummary) { 1243 $o = ''; 1244 1245 $strplural = get_string('modulenameplural', 'assign'); 1246 $strsectionname = $indexsummary->courseformatname; 1247 $strduedate = get_string('duedate', 'assign'); 1248 $strsubmission = get_string('submission', 'assign'); 1249 $strgrade = get_string('gradenoun'); 1250 1251 $table = new \html_table(); 1252 if ($indexsummary->usesections) { 1253 $table->head = array ($strsectionname, $strplural, $strduedate, $strsubmission, $strgrade); 1254 $table->align = array ('left', 'left', 'center', 'right', 'right'); 1255 } else { 1256 $table->head = array ($strplural, $strduedate, $strsubmission, $strgrade); 1257 $table->align = array ('left', 'left', 'center', 'right'); 1258 } 1259 $table->data = array(); 1260 1261 $currentsection = ''; 1262 foreach ($indexsummary->assignments as $info) { 1263 $params = array('id' => $info['cmid']); 1264 $link = \html_writer::link(new \moodle_url('/mod/assign/view.php', $params), 1265 $info['cmname']); 1266 $due = $info['timedue'] ? userdate($info['timedue']) : '-'; 1267 1268 if ($info['cangrade']) { 1269 $params['action'] = 'grading'; 1270 $gradeinfo = \html_writer::link(new \moodle_url('/mod/assign/view.php', $params), 1271 get_string('numberofsubmissionsneedgradinglabel', 'assign', $info['gradeinfo'])); 1272 } else { 1273 $gradeinfo = $info['gradeinfo']; 1274 } 1275 1276 $printsection = ''; 1277 if ($indexsummary->usesections) { 1278 if ($info['sectionname'] !== $currentsection) { 1279 if ($info['sectionname']) { 1280 $printsection = $info['sectionname']; 1281 } 1282 if ($currentsection !== '') { 1283 $table->data[] = 'hr'; 1284 } 1285 $currentsection = $info['sectionname']; 1286 } 1287 } 1288 1289 if ($indexsummary->usesections) { 1290 $row = [$printsection, $link, $due, $info['submissioninfo'], $gradeinfo]; 1291 } else { 1292 $row = [$link, $due, $info['submissioninfo'], $gradeinfo]; 1293 } 1294 $table->data[] = $row; 1295 } 1296 1297 $o .= \html_writer::table($table); 1298 1299 return $o; 1300 } 1301 1302 /** 1303 * Get the time remaining for a submission. 1304 * 1305 * @param \mod_assign\output\assign_submission_status $status 1306 * @return array The first element is the time remaining as a human readable 1307 * string and the second is a CSS class. 1308 */ 1309 protected function get_time_remaining(\mod_assign\output\assign_submission_status $status): array { 1310 $time = time(); 1311 $submission = $status->teamsubmission ? $status->teamsubmission : $status->submission; 1312 $submissionstarted = $submission && property_exists($submission, 'timestarted') && $submission->timestarted; 1313 $timelimitenabled = get_config('assign', 'enabletimelimit') && $status->timelimit > 0 && $submissionstarted; 1314 // Define $duedate as latest between due date and extension - which is a possibility... 1315 $extensionduedate = intval($status->extensionduedate); 1316 $duedate = !empty($extensionduedate) ? max($status->duedate, $extensionduedate) : $status->duedate; 1317 $duedatereached = $duedate > 0 && $duedate - $time <= 0; 1318 $timelimitenabledbeforeduedate = $timelimitenabled && !$duedatereached; 1319 1320 // There is a submission, display the relevant early/late message. 1321 if ($submission && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) { 1322 $latecalculation = $submission->timemodified - ($timelimitenabledbeforeduedate ? $submission->timestarted : 0); 1323 $latethreshold = $timelimitenabledbeforeduedate ? $status->timelimit : $duedate; 1324 $earlystring = $timelimitenabledbeforeduedate ? 'submittedundertime' : 'submittedearly'; 1325 $latestring = $timelimitenabledbeforeduedate ? 'submittedovertime' : 'submittedlate'; 1326 $ontime = $latecalculation <= $latethreshold; 1327 return [ 1328 get_string( 1329 $ontime ? $earlystring : $latestring, 1330 'assign', 1331 format_time($latecalculation - $latethreshold) 1332 ), 1333 $ontime ? 'earlysubmission' : 'latesubmission' 1334 ]; 1335 } 1336 1337 // There is no submission, due date has passed, show assignment is overdue. 1338 if ($duedatereached) { 1339 return [ 1340 get_string( 1341 $status->submissionsenabled ? 'overdue' : 'duedatereached', 1342 'assign', 1343 format_time($time - $duedate) 1344 ), 1345 'overdue' 1346 ]; 1347 } 1348 1349 // An attempt has started and there is a time limit, display the time limit. 1350 if ($timelimitenabled && !empty($submission->timestarted)) { 1351 return [ 1352 (new \assign($status->context, null, null))->get_timelimit_panel($submission), 1353 'timeremaining' 1354 ]; 1355 } 1356 1357 // Assignment is not overdue, and no submission has been made. Just display the due date. 1358 return [get_string('paramtimeremaining', 'assign', format_time($duedate - $time)), 'timeremaining']; 1359 } 1360 1361 /** 1362 * Internal function - creates htmls structure suitable for YUI tree. 1363 * 1364 * @param \assign_files $tree 1365 * @param array $dir 1366 * @return string 1367 */ 1368 protected function htmllize_tree(\assign_files $tree, $dir) { 1369 global $CFG; 1370 $yuiconfig = array(); 1371 $yuiconfig['type'] = 'html'; 1372 1373 if (empty($dir['subdirs']) and empty($dir['files'])) { 1374 return ''; 1375 } 1376 1377 $result = '<ul>'; 1378 foreach ($dir['subdirs'] as $subdir) { 1379 $image = $this->output->pix_icon(file_folder_icon(), 1380 $subdir['dirname'], 1381 'moodle', 1382 array('class'=>'icon')); 1383 $result .= '<li yuiConfig=\'' . json_encode($yuiconfig) . '\'>' . 1384 '<div>' . $image . ' ' . s($subdir['dirname']) . '</div> ' . 1385 $this->htmllize_tree($tree, $subdir) . 1386 '</li>'; 1387 } 1388 1389 foreach ($dir['files'] as $file) { 1390 $filename = $file->get_filename(); 1391 if ($CFG->enableplagiarism) { 1392 require_once($CFG->libdir.'/plagiarismlib.php'); 1393 $plagiarismlinks = plagiarism_get_links(array('userid'=>$file->get_userid(), 1394 'file'=>$file, 1395 'cmid'=>$tree->cm->id, 1396 'course'=>$tree->course)); 1397 } else { 1398 $plagiarismlinks = ''; 1399 } 1400 $image = $this->output->pix_icon(file_file_icon($file), 1401 $filename, 1402 'moodle', 1403 array('class'=>'icon')); 1404 $result .= '<li yuiConfig=\'' . json_encode($yuiconfig) . '\'>' . 1405 '<div>' . 1406 '<div class="fileuploadsubmission">' . $image . ' ' . 1407 html_writer::link($tree->get_file_url($file), $file->get_filename(), [ 1408 'target' => '_blank', 1409 ]) . ' ' . 1410 $plagiarismlinks . ' ' . 1411 $this->get_portfolio_button($tree, $file) . ' ' . 1412 '</div>' . 1413 '<div class="fileuploadsubmissiontime">' . $tree->get_modified_time($file) . '</div>' . 1414 '</div>' . 1415 '</li>'; 1416 } 1417 1418 $result .= '</ul>'; 1419 1420 return $result; 1421 } 1422 1423 /** 1424 * Get the portfolio button content for the specified file. 1425 * 1426 * @param assign_files $tree 1427 * @param stored_file $file 1428 * @return string 1429 */ 1430 protected function get_portfolio_button(assign_files $tree, stored_file $file): string { 1431 global $CFG; 1432 if (empty($CFG->enableportfolios)) { 1433 return ''; 1434 } 1435 1436 if (!has_capability('mod/assign:exportownsubmission', $tree->context)) { 1437 return ''; 1438 } 1439 1440 require_once($CFG->libdir . '/portfoliolib.php'); 1441 1442 $button = new portfolio_add_button(); 1443 $portfolioparams = [ 1444 'cmid' => $tree->cm->id, 1445 'fileid' => $file->get_id(), 1446 ]; 1447 $button->set_callback_options('assign_portfolio_caller', $portfolioparams, 'mod_assign'); 1448 $button->set_format_by_file($file); 1449 1450 return (string) $button->to_html(PORTFOLIO_ADD_ICON_LINK); 1451 } 1452 1453 /** 1454 * Helper method dealing with the fact we can not just fetch the output of flexible_table 1455 * 1456 * @param \flexible_table $table The table to render 1457 * @param int $rowsperpage How many assignments to render in a page 1458 * @param bool $displaylinks - Whether to render links in the table 1459 * (e.g. downloads would not enable this) 1460 * @return string HTML 1461 */ 1462 protected function flexible_table(\flexible_table $table, $rowsperpage, $displaylinks) { 1463 1464 $o = ''; 1465 ob_start(); 1466 $table->out($rowsperpage, $displaylinks); 1467 $o = ob_get_contents(); 1468 ob_end_clean(); 1469 1470 return $o; 1471 } 1472 1473 /** 1474 * Helper method dealing with the fact we can not just fetch the output of moodleforms 1475 * 1476 * @param \moodleform $mform 1477 * @return string HTML 1478 */ 1479 protected function moodleform(\moodleform $mform) { 1480 1481 $o = ''; 1482 ob_start(); 1483 $mform->display(); 1484 $o = ob_get_contents(); 1485 ob_end_clean(); 1486 1487 return $o; 1488 } 1489 1490 /** 1491 * Defer to template. 1492 * 1493 * @param grading_app $app - All the data to render the grading app. 1494 */ 1495 public function render_grading_app(grading_app $app) { 1496 $context = $app->export_for_template($this); 1497 return $this->render_from_template('mod_assign/grading_app', $context); 1498 } 1499 1500 /** 1501 * Renders the submission action menu. 1502 * 1503 * @param \mod_assign\output\actionmenu $actionmenu The actionmenu 1504 * @return string Rendered action menu. 1505 */ 1506 public function submission_actionmenu(\mod_assign\output\actionmenu $actionmenu): string { 1507 $context = $actionmenu->export_for_template($this); 1508 return $this->render_from_template('mod_assign/submission_actionmenu', $context); 1509 } 1510 1511 /** 1512 * Renders the user submission action menu. 1513 * 1514 * @param \mod_assign\output\user_submission_actionmenu $actionmenu The actionmenu 1515 * @return string The rendered action menu. 1516 */ 1517 public function render_user_submission_actionmenu(\mod_assign\output\user_submission_actionmenu $actionmenu): string { 1518 $context = $actionmenu->export_for_template($this); 1519 return $this->render_from_template('mod_assign/user_submission_actionmenu', $context); 1520 } 1521 1522 /** 1523 * Renders the override action menu. 1524 * 1525 * @param \mod_assign\output\override_actionmenu $actionmenu The actionmenu 1526 * @return string The rendered override action menu. 1527 */ 1528 public function render_override_actionmenu(\mod_assign\output\override_actionmenu $actionmenu): string { 1529 $context = $actionmenu->export_for_template($this); 1530 return $this->render_from_template('mod_assign/override_actionmenu', $context); 1531 } 1532 1533 /** 1534 * Renders the grading action menu. 1535 * 1536 * @param \mod_assign\output\grading_actionmenu $actionmenu The actionmenu 1537 * @return string The rendered grading action menu. 1538 */ 1539 public function render_grading_actionmenu(\mod_assign\output\grading_actionmenu $actionmenu): string { 1540 $context = $actionmenu->export_for_template($this); 1541 return $this->render_from_template('mod_assign/grading_actionmenu', $context); 1542 } 1543 1544 /** 1545 * Formats activity intro text. 1546 * 1547 * @param object $assign Instance of assign. 1548 * @param int $cmid Course module ID. 1549 * @return string 1550 */ 1551 public function format_activity_text($assign, $cmid) { 1552 global $CFG; 1553 require_once("$CFG->libdir/filelib.php"); 1554 $context = \context_module::instance($cmid); 1555 $options = array('noclean' => true, 'para' => false, 'filter' => true, 'context' => $context, 'overflowdiv' => true); 1556 $activity = file_rewrite_pluginfile_urls( 1557 $assign->activity, 'pluginfile.php', $context->id, 'mod_assign', ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0); 1558 return trim(format_text($activity, $assign->activityformat, $options, null)); 1559 } 1560 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body