Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * Workshop module renderering methods are defined here
  20   *
  21   * @package    mod_workshop
  22   * @copyright  2009 David Mudrak <david.mudrak@gmail.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  /**
  29   * Workshop module renderer class
  30   *
  31   * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
  32   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  33   */
  34  class mod_workshop_renderer extends plugin_renderer_base {
  35  
  36      ////////////////////////////////////////////////////////////////////////////
  37      // External API - methods to render workshop renderable components
  38      ////////////////////////////////////////////////////////////////////////////
  39  
  40      /**
  41       * Renders workshop message
  42       *
  43       * @param workshop_message $message to display
  44       * @return string html code
  45       */
  46      protected function render_workshop_message(workshop_message $message) {
  47  
  48          $text   = $message->get_message();
  49          $url    = $message->get_action_url();
  50          $label  = $message->get_action_label();
  51  
  52          if (empty($text) and empty($label)) {
  53              return '';
  54          }
  55  
  56          switch ($message->get_type()) {
  57          case workshop_message::TYPE_OK:
  58              $sty = 'ok';
  59              break;
  60          case workshop_message::TYPE_ERROR:
  61              $sty = 'error';
  62              break;
  63          default:
  64              $sty = 'info';
  65          }
  66  
  67          $o = html_writer::tag('span', $message->get_message());
  68  
  69          if (!is_null($url) and !is_null($label)) {
  70              $o .= $this->output->single_button($url, $label, 'get');
  71          }
  72  
  73          return $this->output->container($o, array('message', $sty));
  74      }
  75  
  76  
  77      /**
  78       * Renders full workshop submission
  79       *
  80       * @param workshop_submission $submission
  81       * @return string HTML
  82       */
  83      protected function render_workshop_submission(workshop_submission $submission) {
  84          global $CFG;
  85  
  86          $o  = '';    // output HTML code
  87          $anonymous = $submission->is_anonymous();
  88          $classes = 'submission-full';
  89          if ($anonymous) {
  90              $classes .= ' anonymous';
  91          }
  92          $o .= $this->output->container_start($classes);
  93          $o .= $this->output->container_start('header');
  94  
  95          $title = format_string($submission->title);
  96  
  97          if ($this->page->url != $submission->url) {
  98              $title = html_writer::link($submission->url, $title);
  99          }
 100  
 101          $o .= $this->output->heading($title, 3, 'title');
 102  
 103          if (!$anonymous) {
 104              $author = new stdclass();
 105              $additionalfields = explode(',', user_picture::fields());
 106              $author = username_load_fields_from_object($author, $submission, 'author', $additionalfields);
 107              $userpic            = $this->output->user_picture($author, array('courseid' => $this->page->course->id, 'size' => 64));
 108              $userurl            = new moodle_url('/user/view.php',
 109                                              array('id' => $author->id, 'course' => $this->page->course->id));
 110              $a                  = new stdclass();
 111              $a->name            = fullname($author);
 112              $a->url             = $userurl->out();
 113              $byfullname         = get_string('byfullname', 'workshop', $a);
 114              $oo  = $this->output->container($userpic, 'picture');
 115              $oo .= $this->output->container($byfullname, 'fullname');
 116  
 117              $o .= $this->output->container($oo, 'author');
 118          }
 119  
 120          $created = get_string('userdatecreated', 'workshop', userdate($submission->timecreated));
 121          $o .= $this->output->container($created, 'userdate created');
 122  
 123          if ($submission->timemodified > $submission->timecreated) {
 124              $modified = get_string('userdatemodified', 'workshop', userdate($submission->timemodified));
 125              $o .= $this->output->container($modified, 'userdate modified');
 126          }
 127  
 128          $o .= $this->output->container_end(); // end of header
 129  
 130          $content = file_rewrite_pluginfile_urls($submission->content, 'pluginfile.php', $this->page->context->id,
 131                                                          'mod_workshop', 'submission_content', $submission->id);
 132          $content = format_text($content, $submission->contentformat, array('overflowdiv'=>true));
 133          if (!empty($content)) {
 134              if (!empty($CFG->enableplagiarism)) {
 135                  require_once($CFG->libdir.'/plagiarismlib.php');
 136                  $content .= plagiarism_get_links(array('userid' => $submission->authorid,
 137                      'content' => $submission->content,
 138                      'cmid' => $this->page->cm->id,
 139                      'course' => $this->page->course));
 140              }
 141          }
 142          $o .= $this->output->container($content, 'content');
 143  
 144          $o .= $this->helper_submission_attachments($submission->id, 'html');
 145  
 146          $o .= $this->output->container_end(); // end of submission-full
 147  
 148          return $o;
 149      }
 150  
 151      /**
 152       * Renders short summary of the submission
 153       *
 154       * @param workshop_submission_summary $summary
 155       * @return string text to be echo'ed
 156       */
 157      protected function render_workshop_submission_summary(workshop_submission_summary $summary) {
 158  
 159          $o  = '';    // output HTML code
 160          $anonymous = $summary->is_anonymous();
 161          $classes = 'submission-summary';
 162  
 163          if ($anonymous) {
 164              $classes .= ' anonymous';
 165          }
 166  
 167          $gradestatus = '';
 168  
 169          if ($summary->status == 'notgraded') {
 170              $classes    .= ' notgraded';
 171              $gradestatus = $this->output->container(get_string('nogradeyet', 'workshop'), 'grade-status');
 172  
 173          } else if ($summary->status == 'graded') {
 174              $classes    .= ' graded';
 175              $gradestatus = $this->output->container(get_string('alreadygraded', 'workshop'), 'grade-status');
 176          }
 177  
 178          $o .= $this->output->container_start($classes);  // main wrapper
 179          $o .= html_writer::link($summary->url, format_string($summary->title), array('class' => 'title'));
 180  
 181          if (!$anonymous) {
 182              $author             = new stdClass();
 183              $additionalfields = explode(',', user_picture::fields());
 184              $author = username_load_fields_from_object($author, $summary, 'author', $additionalfields);
 185              $userpic            = $this->output->user_picture($author, array('courseid' => $this->page->course->id, 'size' => 35));
 186              $userurl            = new moodle_url('/user/view.php',
 187                                              array('id' => $author->id, 'course' => $this->page->course->id));
 188              $a                  = new stdClass();
 189              $a->name            = fullname($author);
 190              $a->url             = $userurl->out();
 191              $byfullname         = get_string('byfullname', 'workshop', $a);
 192  
 193              $oo  = $this->output->container($userpic, 'picture');
 194              $oo .= $this->output->container($byfullname, 'fullname');
 195              $o  .= $this->output->container($oo, 'author');
 196          }
 197  
 198          $created = get_string('userdatecreated', 'workshop', userdate($summary->timecreated));
 199          $o .= $this->output->container($created, 'userdate created');
 200  
 201          if ($summary->timemodified > $summary->timecreated) {
 202              $modified = get_string('userdatemodified', 'workshop', userdate($summary->timemodified));
 203              $o .= $this->output->container($modified, 'userdate modified');
 204          }
 205  
 206          $o .= $gradestatus;
 207          $o .= $this->output->container_end(); // end of the main wrapper
 208          return $o;
 209      }
 210  
 211      /**
 212       * Renders full workshop example submission
 213       *
 214       * @param workshop_example_submission $example
 215       * @return string HTML
 216       */
 217      protected function render_workshop_example_submission(workshop_example_submission $example) {
 218  
 219          $o  = '';    // output HTML code
 220          $classes = 'submission-full example';
 221          $o .= $this->output->container_start($classes);
 222          $o .= $this->output->container_start('header');
 223          $o .= $this->output->container(format_string($example->title), array('class' => 'title'));
 224          $o .= $this->output->container_end(); // end of header
 225  
 226          $content = file_rewrite_pluginfile_urls($example->content, 'pluginfile.php', $this->page->context->id,
 227                                                          'mod_workshop', 'submission_content', $example->id);
 228          $content = format_text($content, $example->contentformat, array('overflowdiv'=>true));
 229          $o .= $this->output->container($content, 'content');
 230  
 231          $o .= $this->helper_submission_attachments($example->id, 'html');
 232  
 233          $o .= $this->output->container_end(); // end of submission-full
 234  
 235          return $o;
 236      }
 237  
 238      /**
 239       * Renders short summary of the example submission
 240       *
 241       * @param workshop_example_submission_summary $summary
 242       * @return string text to be echo'ed
 243       */
 244      protected function render_workshop_example_submission_summary(workshop_example_submission_summary $summary) {
 245  
 246          $o  = '';    // output HTML code
 247  
 248          // wrapping box
 249          $o .= $this->output->box_start('generalbox example-summary ' . $summary->status);
 250  
 251          // title
 252          $o .= $this->output->container_start('example-title');
 253          $o .= html_writer::link($summary->url, format_string($summary->title), array('class' => 'title'));
 254  
 255          if ($summary->editable) {
 256              $o .= $this->output->action_icon($summary->editurl, new pix_icon('i/edit', get_string('edit')));
 257          }
 258          $o .= $this->output->container_end();
 259  
 260          // additional info
 261          if ($summary->status == 'notgraded') {
 262              $o .= $this->output->container(get_string('nogradeyet', 'workshop'), 'example-info nograde');
 263          } else {
 264              $o .= $this->output->container(get_string('gradeinfo', 'workshop' , $summary->gradeinfo), 'example-info grade');
 265          }
 266  
 267          // button to assess
 268          $button = new single_button($summary->assessurl, $summary->assesslabel, 'get');
 269          $o .= $this->output->container($this->output->render($button), 'example-actions');
 270  
 271          // end of wrapping box
 272          $o .= $this->output->box_end();
 273  
 274          return $o;
 275      }
 276  
 277      /**
 278       * Renders the user plannner tool
 279       *
 280       * @param workshop_user_plan $plan prepared for the user
 281       * @return string html code to be displayed
 282       */
 283      protected function render_workshop_user_plan(workshop_user_plan $plan) {
 284          $o  = '';    // Output HTML code.
 285          $numberofphases = count($plan->phases);
 286          $o .= html_writer::start_tag('div', array(
 287              'class' => 'userplan',
 288              'aria-labelledby' => 'mod_workshop-userplanheading',
 289              'aria-describedby' => 'mod_workshop-userplanaccessibilitytitle',
 290          ));
 291          $o .= html_writer::span(get_string('userplanaccessibilitytitle', 'workshop', $numberofphases),
 292              'accesshide', array('id' => 'mod_workshop-userplanaccessibilitytitle'));
 293          $o .= html_writer::link('#mod_workshop-userplancurrenttasks', get_string('userplanaccessibilityskip', 'workshop'),
 294              array('class' => 'accesshide'));
 295          foreach ($plan->phases as $phasecode => $phase) {
 296              $o .= html_writer::start_tag('dl', array('class' => 'phase'));
 297              $actions = '';
 298  
 299              if ($phase->active) {
 300                  // Mark the section as the current one.
 301                  $icon = $this->output->pix_icon('i/marked', '', 'moodle', ['role' => 'presentation']);
 302                  $actions .= get_string('userplancurrentphase', 'workshop').' '.$icon;
 303  
 304              } else {
 305                  // Display a control widget to switch to the given phase or mark the phase as the current one.
 306                  foreach ($phase->actions as $action) {
 307                      if ($action->type === 'switchphase') {
 308                          if ($phasecode == workshop::PHASE_ASSESSMENT && $plan->workshop->phase == workshop::PHASE_SUBMISSION
 309                                  && $plan->workshop->phaseswitchassessment) {
 310                              $icon = new pix_icon('i/scheduled', get_string('switchphaseauto', 'mod_workshop'));
 311                          } else {
 312                              $icon = new pix_icon('i/marker', get_string('switchphase'.$phasecode, 'mod_workshop'));
 313                          }
 314                          $actions .= $this->output->action_icon($action->url, $icon, null, null, true);
 315                      }
 316                  }
 317              }
 318  
 319              if (!empty($actions)) {
 320                  $actions = $this->output->container($actions, 'actions');
 321              }
 322              $classes = 'phase' . $phasecode;
 323              if ($phase->active) {
 324                  $title = html_writer::span($phase->title, 'phasetitle', ['id' => 'mod_workshop-userplancurrenttasks']);
 325                  $classes .= ' active';
 326              } else {
 327                  $title = html_writer::span($phase->title, 'phasetitle');
 328                  $classes .= ' nonactive';
 329              }
 330              $o .= html_writer::start_tag('dt', array('class' => $classes));
 331              $o .= $this->output->container($title . $actions);
 332              $o .= html_writer::start_tag('dd', array('class' => $classes. ' phasetasks'));
 333              $o .= $this->helper_user_plan_tasks($phase->tasks);
 334              $o .= html_writer::end_tag('dd');
 335              $o .= html_writer::end_tag('dl');
 336          }
 337          $o .= html_writer::end_tag('div');
 338          return $o;
 339      }
 340  
 341      /**
 342       * Renders the result of the submissions allocation process
 343       *
 344       * @param workshop_allocation_result $result as returned by the allocator's init() method
 345       * @return string HTML to be echoed
 346       */
 347      protected function render_workshop_allocation_result(workshop_allocation_result $result) {
 348          global $CFG;
 349  
 350          $status = $result->get_status();
 351  
 352          if (is_null($status) or $status == workshop_allocation_result::STATUS_VOID) {
 353              debugging('Attempt to render workshop_allocation_result with empty status', DEBUG_DEVELOPER);
 354              return '';
 355          }
 356  
 357          switch ($status) {
 358          case workshop_allocation_result::STATUS_FAILED:
 359              if ($message = $result->get_message()) {
 360                  $message = new workshop_message($message, workshop_message::TYPE_ERROR);
 361              } else {
 362                  $message = new workshop_message(get_string('allocationerror', 'workshop'), workshop_message::TYPE_ERROR);
 363              }
 364              break;
 365  
 366          case workshop_allocation_result::STATUS_CONFIGURED:
 367              if ($message = $result->get_message()) {
 368                  $message = new workshop_message($message, workshop_message::TYPE_INFO);
 369              } else {
 370                  $message = new workshop_message(get_string('allocationconfigured', 'workshop'), workshop_message::TYPE_INFO);
 371              }
 372              break;
 373  
 374          case workshop_allocation_result::STATUS_EXECUTED:
 375              if ($message = $result->get_message()) {
 376                  $message = new workshop_message($message, workshop_message::TYPE_OK);
 377              } else {
 378                  $message = new workshop_message(get_string('allocationdone', 'workshop'), workshop_message::TYPE_OK);
 379              }
 380              break;
 381  
 382          default:
 383              throw new coding_exception('Unknown allocation result status', $status);
 384          }
 385  
 386          // start with the message
 387          $o = $this->render($message);
 388  
 389          // display the details about the process if available
 390          $logs = $result->get_logs();
 391          if (is_array($logs) and !empty($logs)) {
 392              $o .= html_writer::start_tag('ul', array('class' => 'allocation-init-results'));
 393              foreach ($logs as $log) {
 394                  if ($log->type == 'debug' and !$CFG->debugdeveloper) {
 395                      // display allocation debugging messages for developers only
 396                      continue;
 397                  }
 398                  $class = $log->type;
 399                  if ($log->indent) {
 400                      $class .= ' indent';
 401                  }
 402                  $o .= html_writer::tag('li', $log->message, array('class' => $class)).PHP_EOL;
 403              }
 404              $o .= html_writer::end_tag('ul');
 405          }
 406  
 407          return $o;
 408      }
 409  
 410      /**
 411       * Renders the workshop grading report
 412       *
 413       * @param workshop_grading_report $gradingreport
 414       * @return string html code
 415       */
 416      protected function render_workshop_grading_report(workshop_grading_report $gradingreport) {
 417  
 418          $data       = $gradingreport->get_data();
 419          $options    = $gradingreport->get_options();
 420          $grades     = $data->grades;
 421          $userinfo   = $data->userinfo;
 422  
 423          if (empty($grades)) {
 424              return '';
 425          }
 426  
 427          $table = new html_table();
 428          $table->attributes['class'] = 'grading-report table-striped table-hover';
 429  
 430          $sortbyfirstname = $this->helper_sortable_heading(get_string('firstname'), 'firstname', $options->sortby, $options->sorthow);
 431          $sortbylastname = $this->helper_sortable_heading(get_string('lastname'), 'lastname', $options->sortby, $options->sorthow);
 432          if (self::fullname_format() == 'lf') {
 433              $sortbyname = $sortbylastname . ' / ' . $sortbyfirstname;
 434          } else {
 435              $sortbyname = $sortbyfirstname . ' / ' . $sortbylastname;
 436          }
 437  
 438          $sortbysubmisstiontitle = $this->helper_sortable_heading(get_string('submission', 'workshop'), 'submissiontitle',
 439                  $options->sortby, $options->sorthow);
 440          $sortbysubmisstionlastmodified = $this->helper_sortable_heading(get_string('submissionlastmodified', 'workshop'),
 441                  'submissionmodified', $options->sortby, $options->sorthow);
 442          $sortbysubmisstion = $sortbysubmisstiontitle . ' / ' . $sortbysubmisstionlastmodified;
 443  
 444          $table->head = array();
 445          $table->head[] = $sortbyname;
 446          $table->head[] = $sortbysubmisstion;
 447  
 448          // If we are in submission phase ignore the following headers (columns).
 449          if ($options->workshopphase != workshop::PHASE_SUBMISSION) {
 450              $table->head[] = $this->helper_sortable_heading(get_string('receivedgrades', 'workshop'));
 451              if ($options->showsubmissiongrade) {
 452                  $table->head[] = $this->helper_sortable_heading(get_string('submissiongradeof', 'workshop', $data->maxgrade),
 453                          'submissiongrade', $options->sortby, $options->sorthow);
 454              }
 455              $table->head[] = $this->helper_sortable_heading(get_string('givengrades', 'workshop'));
 456              if ($options->showgradinggrade) {
 457                  $table->head[] = $this->helper_sortable_heading(get_string('gradinggradeof', 'workshop', $data->maxgradinggrade),
 458                          'gradinggrade', $options->sortby, $options->sorthow);
 459              }
 460          }
 461          $table->rowclasses  = array();
 462          $table->colclasses  = array();
 463          $table->data        = array();
 464  
 465          foreach ($grades as $participant) {
 466              $numofreceived  = count($participant->reviewedby);
 467              $numofgiven     = count($participant->reviewerof);
 468              $published      = $participant->submissionpublished;
 469  
 470              // compute the number of <tr> table rows needed to display this participant
 471              if ($numofreceived > 0 and $numofgiven > 0) {
 472                  $numoftrs       = workshop::lcm($numofreceived, $numofgiven);
 473                  $spanreceived   = $numoftrs / $numofreceived;
 474                  $spangiven      = $numoftrs / $numofgiven;
 475              } elseif ($numofreceived == 0 and $numofgiven > 0) {
 476                  $numoftrs       = $numofgiven;
 477                  $spanreceived   = $numoftrs;
 478                  $spangiven      = $numoftrs / $numofgiven;
 479              } elseif ($numofreceived > 0 and $numofgiven == 0) {
 480                  $numoftrs       = $numofreceived;
 481                  $spanreceived   = $numoftrs / $numofreceived;
 482                  $spangiven      = $numoftrs;
 483              } else {
 484                  $numoftrs       = 1;
 485                  $spanreceived   = 1;
 486                  $spangiven      = 1;
 487              }
 488  
 489              for ($tr = 0; $tr < $numoftrs; $tr++) {
 490                  $row = new html_table_row();
 491                  if ($published) {
 492                      $row->attributes['class'] = 'published';
 493                  }
 494                  // column #1 - participant - spans over all rows
 495                  if ($tr == 0) {
 496                      $cell = new html_table_cell();
 497                      $cell->text = $this->helper_grading_report_participant($participant, $userinfo);
 498                      $cell->rowspan = $numoftrs;
 499                      $cell->attributes['class'] = 'participant';
 500                      $row->cells[] = $cell;
 501                  }
 502                  // column #2 - submission - spans over all rows
 503                  if ($tr == 0) {
 504                      $cell = new html_table_cell();
 505                      $cell->text = $this->helper_grading_report_submission($participant);
 506                      $cell->rowspan = $numoftrs;
 507                      $cell->attributes['class'] = 'submission';
 508                      $row->cells[] = $cell;
 509                  }
 510  
 511                  // If we are in submission phase ignore the following columns.
 512                  if ($options->workshopphase == workshop::PHASE_SUBMISSION) {
 513                      $table->data[] = $row;
 514                      continue;
 515                  }
 516  
 517                  // column #3 - received grades
 518                  if ($tr % $spanreceived == 0) {
 519                      $idx = intval($tr / $spanreceived);
 520                      $assessment = self::array_nth($participant->reviewedby, $idx);
 521                      $cell = new html_table_cell();
 522                      $cell->text = $this->helper_grading_report_assessment($assessment, $options->showreviewernames, $userinfo,
 523                              get_string('gradereceivedfrom', 'workshop'));
 524                      $cell->rowspan = $spanreceived;
 525                      $cell->attributes['class'] = 'receivedgrade';
 526                      if (is_null($assessment) or is_null($assessment->grade)) {
 527                          $cell->attributes['class'] .= ' null';
 528                      } else {
 529                          $cell->attributes['class'] .= ' notnull';
 530                      }
 531                      $row->cells[] = $cell;
 532                  }
 533                  // column #4 - total grade for submission
 534                  if ($options->showsubmissiongrade and $tr == 0) {
 535                      $cell = new html_table_cell();
 536                      $cell->text = $this->helper_grading_report_grade($participant->submissiongrade, $participant->submissiongradeover);
 537                      $cell->rowspan = $numoftrs;
 538                      $cell->attributes['class'] = 'submissiongrade';
 539                      $row->cells[] = $cell;
 540                  }
 541                  // column #5 - given grades
 542                  if ($tr % $spangiven == 0) {
 543                      $idx = intval($tr / $spangiven);
 544                      $assessment = self::array_nth($participant->reviewerof, $idx);
 545                      $cell = new html_table_cell();
 546                      $cell->text = $this->helper_grading_report_assessment($assessment, $options->showauthornames, $userinfo,
 547                              get_string('gradegivento', 'workshop'));
 548                      $cell->rowspan = $spangiven;
 549                      $cell->attributes['class'] = 'givengrade';
 550                      if (is_null($assessment) or is_null($assessment->grade)) {
 551                          $cell->attributes['class'] .= ' null';
 552                      } else {
 553                          $cell->attributes['class'] .= ' notnull';
 554                      }
 555                      $row->cells[] = $cell;
 556                  }
 557                  // column #6 - total grade for assessment
 558                  if ($options->showgradinggrade and $tr == 0) {
 559                      $cell = new html_table_cell();
 560                      $cell->text = $this->helper_grading_report_grade($participant->gradinggrade);
 561                      $cell->rowspan = $numoftrs;
 562                      $cell->attributes['class'] = 'gradinggrade';
 563                      $row->cells[] = $cell;
 564                  }
 565  
 566                  $table->data[] = $row;
 567              }
 568          }
 569  
 570          return html_writer::table($table);
 571      }
 572  
 573      /**
 574       * Renders the feedback for the author of the submission
 575       *
 576       * @param workshop_feedback_author $feedback
 577       * @return string HTML
 578       */
 579      protected function render_workshop_feedback_author(workshop_feedback_author $feedback) {
 580          return $this->helper_render_feedback($feedback);
 581      }
 582  
 583      /**
 584       * Renders the feedback for the reviewer of the submission
 585       *
 586       * @param workshop_feedback_reviewer $feedback
 587       * @return string HTML
 588       */
 589      protected function render_workshop_feedback_reviewer(workshop_feedback_reviewer $feedback) {
 590          return $this->helper_render_feedback($feedback);
 591      }
 592  
 593      /**
 594       * Helper method to rendering feedback
 595       *
 596       * @param workshop_feedback_author|workshop_feedback_reviewer $feedback
 597       * @return string HTML
 598       */
 599      private function helper_render_feedback($feedback) {
 600  
 601          $o  = '';    // output HTML code
 602          $o .= $this->output->container_start('feedback feedbackforauthor');
 603          $o .= $this->output->container_start('header');
 604          $o .= $this->output->heading(get_string('feedbackby', 'workshop', s(fullname($feedback->get_provider()))), 3, 'title');
 605  
 606          $userpic = $this->output->user_picture($feedback->get_provider(), array('courseid' => $this->page->course->id, 'size' => 32));
 607          $o .= $this->output->container($userpic, 'picture');
 608          $o .= $this->output->container_end(); // end of header
 609  
 610          $content = format_text($feedback->get_content(), $feedback->get_format(), array('overflowdiv' => true));
 611          $o .= $this->output->container($content, 'content');
 612  
 613          $o .= $this->output->container_end();
 614  
 615          return $o;
 616      }
 617  
 618      /**
 619       * Renders the full assessment
 620       *
 621       * @param workshop_assessment $assessment
 622       * @return string HTML
 623       */
 624      protected function render_workshop_assessment(workshop_assessment $assessment) {
 625  
 626          $o = ''; // output HTML code
 627          $anonymous = is_null($assessment->reviewer);
 628          $classes = 'assessment-full';
 629          if ($anonymous) {
 630              $classes .= ' anonymous';
 631          }
 632  
 633          $o .= $this->output->container_start($classes);
 634          $o .= $this->output->container_start('header');
 635  
 636          if (!empty($assessment->title)) {
 637              $title = s($assessment->title);
 638          } else {
 639              $title = get_string('assessment', 'workshop');
 640          }
 641          if (($assessment->url instanceof moodle_url) and ($this->page->url != $assessment->url)) {
 642              $o .= $this->output->container(html_writer::link($assessment->url, $title), 'title');
 643          } else {
 644              $o .= $this->output->container($title, 'title');
 645          }
 646  
 647          if (!$anonymous) {
 648              $reviewer   = $assessment->reviewer;
 649              $userpic    = $this->output->user_picture($reviewer, array('courseid' => $this->page->course->id, 'size' => 32));
 650  
 651              $userurl    = new moodle_url('/user/view.php',
 652                                         array('id' => $reviewer->id, 'course' => $this->page->course->id));
 653              $a          = new stdClass();
 654              $a->name    = fullname($reviewer);
 655              $a->url     = $userurl->out();
 656              $byfullname = get_string('assessmentby', 'workshop', $a);
 657              $oo         = $this->output->container($userpic, 'picture');
 658              $oo        .= $this->output->container($byfullname, 'fullname');
 659  
 660              $o .= $this->output->container($oo, 'reviewer');
 661          }
 662  
 663          if (is_null($assessment->realgrade)) {
 664              $o .= $this->output->container(
 665                  get_string('notassessed', 'workshop'),
 666                  'grade nograde'
 667              );
 668          } else {
 669              $a              = new stdClass();
 670              $a->max         = $assessment->maxgrade;
 671              $a->received    = $assessment->realgrade;
 672              $o .= $this->output->container(
 673                  get_string('gradeinfo', 'workshop', $a),
 674                  'grade'
 675              );
 676  
 677              if (!is_null($assessment->weight) and $assessment->weight != 1) {
 678                  $o .= $this->output->container(
 679                      get_string('weightinfo', 'workshop', $assessment->weight),
 680                      'weight'
 681                  );
 682              }
 683          }
 684  
 685          $o .= $this->output->container_start('actions');
 686          foreach ($assessment->actions as $action) {
 687              $o .= $this->output->single_button($action->url, $action->label, $action->method);
 688          }
 689          $o .= $this->output->container_end(); // actions
 690  
 691          $o .= $this->output->container_end(); // header
 692  
 693          if (!is_null($assessment->form)) {
 694              $o .= print_collapsible_region_start('assessment-form-wrapper', uniqid('workshop-assessment'),
 695                      get_string('assessmentform', 'workshop'), 'workshop-viewlet-assessmentform-collapsed', false, true);
 696              $o .= $this->output->container(self::moodleform($assessment->form), 'assessment-form');
 697              $o .= print_collapsible_region_end(true);
 698  
 699              if (!$assessment->form->is_editable()) {
 700                  $o .= $this->overall_feedback($assessment);
 701              }
 702          }
 703  
 704          $o .= $this->output->container_end(); // main wrapper
 705  
 706          return $o;
 707      }
 708  
 709      /**
 710       * Renders the assessment of an example submission
 711       *
 712       * @param workshop_example_assessment $assessment
 713       * @return string HTML
 714       */
 715      protected function render_workshop_example_assessment(workshop_example_assessment $assessment) {
 716          return $this->render_workshop_assessment($assessment);
 717      }
 718  
 719      /**
 720       * Renders the reference assessment of an example submission
 721       *
 722       * @param workshop_example_reference_assessment $assessment
 723       * @return string HTML
 724       */
 725      protected function render_workshop_example_reference_assessment(workshop_example_reference_assessment $assessment) {
 726          return $this->render_workshop_assessment($assessment);
 727      }
 728  
 729      /**
 730       * Renders the overall feedback for the author of the submission
 731       *
 732       * @param workshop_assessment $assessment
 733       * @return string HTML
 734       */
 735      protected function overall_feedback(workshop_assessment $assessment) {
 736  
 737          $content = $assessment->get_overall_feedback_content();
 738  
 739          if ($content === false) {
 740              return '';
 741          }
 742  
 743          $o = '';
 744  
 745          if (!is_null($content)) {
 746              $o .= $this->output->container($content, 'content');
 747          }
 748  
 749          $attachments = $assessment->get_overall_feedback_attachments();
 750  
 751          if (!empty($attachments)) {
 752              $o .= $this->output->container_start('attachments');
 753              $images = '';
 754              $files = '';
 755              foreach ($attachments as $attachment) {
 756                  $icon = $this->output->pix_icon(file_file_icon($attachment), get_mimetype_description($attachment),
 757                      'moodle', array('class' => 'icon'));
 758                  $link = html_writer::link($attachment->fileurl, $icon.' '.substr($attachment->filepath.$attachment->filename, 1));
 759                  if (file_mimetype_in_typegroup($attachment->mimetype, 'web_image')) {
 760                      $preview = html_writer::empty_tag('img', array('src' => $attachment->previewurl, 'alt' => '', 'class' => 'preview'));
 761                      $preview = html_writer::tag('a', $preview, array('href' => $attachment->fileurl));
 762                      $images .= $this->output->container($preview);
 763                  } else {
 764                      $files .= html_writer::tag('li', $link, array('class' => $attachment->mimetype));
 765                  }
 766              }
 767              if ($images) {
 768                  $images = $this->output->container($images, 'images');
 769              }
 770  
 771              if ($files) {
 772                  $files = html_writer::tag('ul', $files, array('class' => 'files'));
 773              }
 774  
 775              $o .= $images.$files;
 776              $o .= $this->output->container_end();
 777          }
 778  
 779          if ($o === '') {
 780              return '';
 781          }
 782  
 783          $o = $this->output->box($o, 'overallfeedback');
 784          $o = print_collapsible_region($o, 'overall-feedback-wrapper', uniqid('workshop-overall-feedback'),
 785                  get_string('overallfeedback', 'workshop'), 'workshop-viewlet-overallfeedback-collapsed', false, true);
 786  
 787          return $o;
 788      }
 789  
 790      /**
 791       * Renders a perpage selector for workshop listings
 792       *
 793       * The scripts using this have to define the $PAGE->url prior to calling this
 794       * and deal with eventually submitted value themselves.
 795       *
 796       * @param int $current current value of the perpage parameter
 797       * @return string HTML
 798       */
 799      public function perpage_selector($current=10) {
 800  
 801          $options = array();
 802          foreach (array(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 1000) as $option) {
 803              if ($option != $current) {
 804                  $options[$option] = $option;
 805              }
 806          }
 807          $select = new single_select($this->page->url, 'perpage', $options, '', array('' => get_string('showingperpagechange', 'mod_workshop')));
 808          $select->label = get_string('showingperpage', 'mod_workshop', $current);
 809          $select->method = 'post';
 810  
 811          return $this->output->container($this->output->render($select), 'perpagewidget');
 812      }
 813  
 814      /**
 815       * Renders the user's final grades
 816       *
 817       * @param workshop_final_grades $grades with the info about grades in the gradebook
 818       * @return string HTML
 819       */
 820      protected function render_workshop_final_grades(workshop_final_grades $grades) {
 821  
 822          $out = html_writer::start_tag('div', array('class' => 'finalgrades'));
 823  
 824          if (!empty($grades->submissiongrade)) {
 825              $cssclass = 'grade submissiongrade';
 826              if ($grades->submissiongrade->hidden) {
 827                  $cssclass .= ' hiddengrade';
 828              }
 829              $out .= html_writer::tag(
 830                  'div',
 831                  html_writer::tag('div', get_string('submissiongrade', 'mod_workshop'), array('class' => 'gradetype')) .
 832                  html_writer::tag('div', $grades->submissiongrade->str_long_grade, array('class' => 'gradevalue')),
 833                  array('class' => $cssclass)
 834              );
 835          }
 836  
 837          if (!empty($grades->assessmentgrade)) {
 838              $cssclass = 'grade assessmentgrade';
 839              if ($grades->assessmentgrade->hidden) {
 840                  $cssclass .= ' hiddengrade';
 841              }
 842              $out .= html_writer::tag(
 843                  'div',
 844                  html_writer::tag('div', get_string('gradinggrade', 'mod_workshop'), array('class' => 'gradetype')) .
 845                  html_writer::tag('div', $grades->assessmentgrade->str_long_grade, array('class' => 'gradevalue')),
 846                  array('class' => $cssclass)
 847              );
 848          }
 849  
 850          $out .= html_writer::end_tag('div');
 851  
 852          return $out;
 853      }
 854  
 855      ////////////////////////////////////////////////////////////////////////////
 856      // Internal rendering helper methods
 857      ////////////////////////////////////////////////////////////////////////////
 858  
 859      /**
 860       * Renders a list of files attached to the submission
 861       *
 862       * If format==html, then format a html string. If format==text, then format a text-only string.
 863       * Otherwise, returns html for non-images and html to display the image inline.
 864       *
 865       * @param int $submissionid submission identifier
 866       * @param string format the format of the returned string - html|text
 867       * @return string formatted text to be echoed
 868       */
 869      protected function helper_submission_attachments($submissionid, $format = 'html') {
 870          global $CFG;
 871          require_once($CFG->libdir.'/filelib.php');
 872  
 873          $fs     = get_file_storage();
 874          $ctx    = $this->page->context;
 875          $files  = $fs->get_area_files($ctx->id, 'mod_workshop', 'submission_attachment', $submissionid);
 876  
 877          $outputimgs     = '';   // images to be displayed inline
 878          $outputfiles    = '';   // list of attachment files
 879  
 880          foreach ($files as $file) {
 881              if ($file->is_directory()) {
 882                  continue;
 883              }
 884  
 885              $filepath   = $file->get_filepath();
 886              $filename   = $file->get_filename();
 887              $fileurl    = moodle_url::make_pluginfile_url($ctx->id, 'mod_workshop', 'submission_attachment',
 888                              $submissionid, $filepath, $filename, true);
 889              $embedurl   = moodle_url::make_pluginfile_url($ctx->id, 'mod_workshop', 'submission_attachment',
 890                              $submissionid, $filepath, $filename, false);
 891              $embedurl   = new moodle_url($embedurl, array('preview' => 'bigthumb'));
 892              $type       = $file->get_mimetype();
 893              $image      = $this->output->pix_icon(file_file_icon($file), get_mimetype_description($file), 'moodle', array('class' => 'icon'));
 894  
 895              $linkhtml   = html_writer::link($fileurl, $image . substr($filepath, 1) . $filename);
 896              $linktxt    = "$filename [$fileurl]";
 897  
 898              if ($format == 'html') {
 899                  if (file_mimetype_in_typegroup($type, 'web_image')) {
 900                      $preview     = html_writer::empty_tag('img', array('src' => $embedurl, 'alt' => '', 'class' => 'preview'));
 901                      $preview     = html_writer::tag('a', $preview, array('href' => $fileurl));
 902                      $outputimgs .= $this->output->container($preview);
 903  
 904                  } else {
 905                      $outputfiles .= html_writer::tag('li', $linkhtml, array('class' => $type));
 906                  }
 907  
 908              } else if ($format == 'text') {
 909                  $outputfiles .= $linktxt . PHP_EOL;
 910              }
 911  
 912              if (!empty($CFG->enableplagiarism)) {
 913                  require_once($CFG->libdir.'/plagiarismlib.php');
 914                  $outputfiles .= plagiarism_get_links(array('userid' => $file->get_userid(),
 915                      'file' => $file,
 916                      'cmid' => $this->page->cm->id,
 917                      'course' => $this->page->course->id));
 918              }
 919          }
 920  
 921          if ($format == 'html') {
 922              if ($outputimgs) {
 923                  $outputimgs = $this->output->container($outputimgs, 'images');
 924              }
 925  
 926              if ($outputfiles) {
 927                  $outputfiles = html_writer::tag('ul', $outputfiles, array('class' => 'files'));
 928              }
 929  
 930              return $this->output->container($outputimgs . $outputfiles, 'attachments');
 931  
 932          } else {
 933              return $outputfiles;
 934          }
 935      }
 936  
 937      /**
 938       * Renders the tasks for the single phase in the user plan
 939       *
 940       * @param stdClass $tasks
 941       * @return string html code
 942       */
 943      protected function helper_user_plan_tasks(array $tasks) {
 944          $out = '';
 945          foreach ($tasks as $taskcode => $task) {
 946              $classes = '';
 947              $accessibilitytext = '';
 948              $icon = null;
 949              if ($task->completed === true) {
 950                  $classes .= ' completed';
 951                  $accessibilitytext .= get_string('taskdone', 'workshop') . ' ';
 952              } else if ($task->completed === false) {
 953                  $classes .= ' fail';
 954                  $accessibilitytext .= get_string('taskfail', 'workshop') . ' ';
 955              } else if ($task->completed === 'info') {
 956                  $classes .= ' info';
 957                  $accessibilitytext .= get_string('taskinfo', 'workshop') . ' ';
 958              } else {
 959                  $accessibilitytext .= get_string('tasktodo', 'workshop') . ' ';
 960              }
 961              if (is_null($task->link)) {
 962                  $title = html_writer::tag('span', $accessibilitytext, array('class' => 'accesshide'));
 963                  $title .= $task->title;
 964              } else {
 965                  $title = html_writer::tag('span', $accessibilitytext, array('class' => 'accesshide'));
 966                  $title .= html_writer::link($task->link, $task->title);
 967              }
 968              $title = $this->output->container($title, 'title');
 969              $details = $this->output->container($task->details, 'details');
 970              $out .= html_writer::tag('li', $title . $details, array('class' => $classes));
 971          }
 972          if ($out) {
 973              $out = html_writer::tag('ul', $out, array('class' => 'tasks'));
 974          }
 975          return $out;
 976      }
 977  
 978      /**
 979       * Renders a text with icons to sort by the given column
 980       *
 981       * This is intended for table headings.
 982       *
 983       * @param string $text    The heading text
 984       * @param string $sortid  The column id used for sorting
 985       * @param string $sortby  Currently sorted by (column id)
 986       * @param string $sorthow Currently sorted how (ASC|DESC)
 987       *
 988       * @return string
 989       */
 990      protected function helper_sortable_heading($text, $sortid=null, $sortby=null, $sorthow=null) {
 991  
 992          $out = html_writer::tag('span', $text, array('class'=>'text'));
 993  
 994          if (!is_null($sortid)) {
 995              if ($sortby !== $sortid or $sorthow !== 'ASC') {
 996                  $url = new moodle_url($this->page->url);
 997                  $url->params(array('sortby' => $sortid, 'sorthow' => 'ASC'));
 998                  $out .= $this->output->action_icon($url, new pix_icon('t/sort_asc', get_string('sortasc', 'workshop')),
 999                      null, array('class' => 'iconsort sort asc'));
1000              }
1001              if ($sortby !== $sortid or $sorthow !== 'DESC') {
1002                  $url = new moodle_url($this->page->url);
1003                  $url->params(array('sortby' => $sortid, 'sorthow' => 'DESC'));
1004                  $out .= $this->output->action_icon($url, new pix_icon('t/sort_desc', get_string('sortdesc', 'workshop')),
1005                      null, array('class' => 'iconsort sort desc'));
1006              }
1007          }
1008          return $out;
1009  }
1010  
1011      /**
1012       * @param stdClass $participant
1013       * @param array $userinfo
1014       * @return string
1015       */
1016      protected function helper_grading_report_participant(stdclass $participant, array $userinfo) {
1017          $userid = $participant->userid;
1018          $out  = $this->output->user_picture($userinfo[$userid], array('courseid' => $this->page->course->id, 'size' => 35));
1019          $out .= html_writer::tag('span', fullname($userinfo[$userid]));
1020  
1021          return $out;
1022      }
1023  
1024      /**
1025       * @param stdClass $participant
1026       * @return string
1027       */
1028      protected function helper_grading_report_submission(stdclass $participant) {
1029          global $CFG;
1030  
1031          if (is_null($participant->submissionid)) {
1032              $out = $this->output->container(get_string('nosubmissionfound', 'workshop'), 'info');
1033          } else {
1034              $url = new moodle_url('/mod/workshop/submission.php',
1035                                    array('cmid' => $this->page->context->instanceid, 'id' => $participant->submissionid));
1036              $out = html_writer::link($url, format_string($participant->submissiontitle), array('class'=>'title'));
1037  
1038              $lastmodified = get_string('userdatemodified', 'workshop', userdate($participant->submissionmodified));
1039              $out .= html_writer::tag('div', $lastmodified, array('class' => 'lastmodified'));
1040          }
1041  
1042          return $out;
1043      }
1044  
1045      /**
1046       * @todo Highlight the nulls
1047       * @param stdClass|null $assessment
1048       * @param bool $shownames
1049       * @param string $separator between the grade and the reviewer/author
1050       * @return string
1051       */
1052      protected function helper_grading_report_assessment($assessment, $shownames, array $userinfo, $separator) {
1053          global $CFG;
1054  
1055          if (is_null($assessment)) {
1056              return get_string('nullgrade', 'workshop');
1057          }
1058          $a = new stdclass();
1059          $a->grade = is_null($assessment->grade) ? get_string('nullgrade', 'workshop') : $assessment->grade;
1060          $a->gradinggrade = is_null($assessment->gradinggrade) ? get_string('nullgrade', 'workshop') : $assessment->gradinggrade;
1061          $a->weight = $assessment->weight;
1062          // grrr the following logic should really be handled by a future language pack feature
1063          if (is_null($assessment->gradinggradeover)) {
1064              if ($a->weight == 1) {
1065                  $grade = get_string('formatpeergrade', 'workshop', $a);
1066              } else {
1067                  $grade = get_string('formatpeergradeweighted', 'workshop', $a);
1068              }
1069          } else {
1070              $a->gradinggradeover = $assessment->gradinggradeover;
1071              if ($a->weight == 1) {
1072                  $grade = get_string('formatpeergradeover', 'workshop', $a);
1073              } else {
1074                  $grade = get_string('formatpeergradeoverweighted', 'workshop', $a);
1075              }
1076          }
1077          $url = new moodle_url('/mod/workshop/assessment.php',
1078                                array('asid' => $assessment->assessmentid));
1079          $grade = html_writer::link($url, $grade, array('class'=>'grade'));
1080  
1081          if ($shownames) {
1082              $userid = $assessment->userid;
1083              $name   = $this->output->user_picture($userinfo[$userid], array('courseid' => $this->page->course->id, 'size' => 16));
1084              $name  .= html_writer::tag('span', fullname($userinfo[$userid]), array('class' => 'fullname'));
1085              $name   = $separator . html_writer::tag('span', $name, array('class' => 'user'));
1086          } else {
1087              $name   = '';
1088          }
1089  
1090          return $this->output->container($grade . $name, 'assessmentdetails');
1091      }
1092  
1093      /**
1094       * Formats the aggreagated grades
1095       */
1096      protected function helper_grading_report_grade($grade, $over=null) {
1097          $a = new stdclass();
1098          $a->grade = is_null($grade) ? get_string('nullgrade', 'workshop') : $grade;
1099          if (is_null($over)) {
1100              $text = get_string('formataggregatedgrade', 'workshop', $a);
1101          } else {
1102              $a->over = is_null($over) ? get_string('nullgrade', 'workshop') : $over;
1103              $text = get_string('formataggregatedgradeover', 'workshop', $a);
1104          }
1105          return $text;
1106      }
1107  
1108      ////////////////////////////////////////////////////////////////////////////
1109      // Static helpers
1110      ////////////////////////////////////////////////////////////////////////////
1111  
1112      /**
1113       * Helper method dealing with the fact we can not just fetch the output of moodleforms
1114       *
1115       * @param moodleform $mform
1116       * @return string HTML
1117       */
1118      protected static function moodleform(moodleform $mform) {
1119  
1120          ob_start();
1121          $mform->display();
1122          $o = ob_get_contents();
1123          ob_end_clean();
1124  
1125          return $o;
1126      }
1127  
1128      /**
1129       * Helper function returning the n-th item of the array
1130       *
1131       * @param array $a
1132       * @param int   $n from 0 to m, where m is th number of items in the array
1133       * @return mixed the $n-th element of $a
1134       */
1135      protected static function array_nth(array $a, $n) {
1136          $keys = array_keys($a);
1137          if ($n < 0 or $n > count($keys) - 1) {
1138              return null;
1139          }
1140          $key = $keys[$n];
1141          return $a[$key];
1142      }
1143  
1144      /**
1145       * Tries to guess the fullname format set at the site
1146       *
1147       * @return string fl|lf
1148       */
1149      protected static function fullname_format() {
1150          $fake = new stdclass(); // fake user
1151          $fake->lastname = 'LLLL';
1152          $fake->firstname = 'FFFF';
1153          $fullname = get_string('fullnamedisplay', '', $fake);
1154          if (strpos($fullname, 'LLLL') < strpos($fullname, 'FFFF')) {
1155              return 'lf';
1156          } else {
1157              return 'fl';
1158          }
1159      }
1160  }