Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

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