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