Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401]

   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   * Contains renderer used for displaying rubric
  19   *
  20   * @package    gradingform_rubric
  21   * @copyright  2011 Marina Glancy
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  /**
  28   * Grading method plugin renderer
  29   *
  30   * @package    gradingform_rubric
  31   * @copyright  2011 Marina Glancy
  32   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  33   */
  34  class gradingform_rubric_renderer extends plugin_renderer_base {
  35  
  36      /**
  37       * This function returns html code for displaying criterion. Depending on $mode it may be the
  38       * code to edit rubric, to preview the rubric, to evaluate somebody or to review the evaluation.
  39       *
  40       * This function may be called from display_rubric() to display the whole rubric, or it can be
  41       * called by itself to return a template used by JavaScript to add new empty criteria to the
  42       * rubric being designed.
  43       * In this case it will use macros like {NAME}, {LEVELS}, {CRITERION-id}, etc.
  44       *
  45       * When overriding this function it is very important to remember that all elements of html
  46       * form (in edit or evaluate mode) must have the name $elementname.
  47       *
  48       * Also JavaScript relies on the class names of elements and when developer changes them
  49       * script might stop working.
  50       *
  51       * @param int $mode rubric display mode, see {@link gradingform_rubric_controller}
  52       * @param array $options display options for this rubric, defaults are: {@link gradingform_rubric_controller::get_default_options()}
  53       * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
  54       * @param array|null $criterion criterion data
  55       * @param string $levelsstr evaluated templates for this criterion levels
  56       * @param array|null $value (only in view mode) teacher's feedback on this criterion
  57       * @return string
  58       */
  59      public function criterion_template($mode, $options, $elementname = '{NAME}', $criterion = null, $levelsstr = '{LEVELS}', $value = null) {
  60          // TODO MDL-31235 description format, remark format
  61          if ($criterion === null || !is_array($criterion) || !array_key_exists('id', $criterion)) {
  62              $criterion = array('id' => '{CRITERION-id}', 'description' => '{CRITERION-description}', 'sortorder' => '{CRITERION-sortorder}', 'class' => '{CRITERION-class}');
  63          } else {
  64              foreach (array('sortorder', 'description', 'class') as $key) {
  65                  // set missing array elements to empty strings to avoid warnings
  66                  if (!array_key_exists($key, $criterion)) {
  67                      $criterion[$key] = '';
  68                  }
  69              }
  70          }
  71          $criteriontemplate = html_writer::start_tag('tr', array('class' => 'criterion'. $criterion['class'], 'id' => '{NAME}-criteria-{CRITERION-id}'));
  72          if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) {
  73              $criteriontemplate .= html_writer::start_tag('td', array('class' => 'controls'));
  74              foreach (array('moveup', 'delete', 'movedown', 'duplicate') as $key) {
  75                  $value = get_string('criterion'.$key, 'gradingform_rubric');
  76                  $button = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[criteria][{CRITERION-id}]['.$key.']',
  77                      'id' => '{NAME}-criteria-{CRITERION-id}-'.$key, 'value' => $value));
  78                  $criteriontemplate .= html_writer::tag('div', $button, array('class' => $key));
  79              }
  80              $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
  81                                                                          'name' => '{NAME}[criteria][{CRITERION-id}][sortorder]',
  82                                                                          'value' => $criterion['sortorder']));
  83              $criteriontemplate .= html_writer::end_tag('td'); // .controls
  84  
  85              // Criterion description text area.
  86              $descriptiontextareaparams = array(
  87                  'name' => '{NAME}[criteria][{CRITERION-id}][description]',
  88                  'id' => '{NAME}-criteria-{CRITERION-id}-description',
  89                  'aria-label' => get_string('criterion', 'gradingform_rubric', ''),
  90                  'cols' => '10', 'rows' => '5'
  91              );
  92              $description = html_writer::tag('textarea', s($criterion['description']), $descriptiontextareaparams);
  93          } else {
  94              if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) {
  95                  $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][sortorder]', 'value' => $criterion['sortorder']));
  96                  $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][description]', 'value' => $criterion['description']));
  97              }
  98              $description = s($criterion['description']);
  99          }
 100          $descriptionclass = 'description';
 101          if (isset($criterion['error_description'])) {
 102              $descriptionclass .= ' error';
 103          }
 104  
 105          // Description cell params.
 106          $descriptiontdparams = array(
 107              'class' => $descriptionclass,
 108              'id' => '{NAME}-criteria-{CRITERION-id}-description-cell'
 109          );
 110          if ($mode != gradingform_rubric_controller::DISPLAY_EDIT_FULL &&
 111              $mode != gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) {
 112              // Set description's cell as tab-focusable.
 113              $descriptiontdparams['tabindex'] = '0';
 114              // Set label for the criterion cell.
 115              $descriptiontdparams['aria-label'] = get_string('criterion', 'gradingform_rubric', s($criterion['description']));
 116          }
 117  
 118          // Description cell.
 119          $criteriontemplate .= html_writer::tag('td', $description, $descriptiontdparams);
 120  
 121          // Levels table.
 122          $levelsrowparams = [
 123              'id' => '{NAME}-criteria-{CRITERION-id}-levels',
 124              'aria-label' => get_string('levelsgroup', 'gradingform_rubric'),
 125          ];
 126          // Add radiogroup role only when not previewing or editing.
 127          $isradiogroup = !in_array($mode, [
 128              gradingform_rubric_controller::DISPLAY_EDIT_FULL,
 129              gradingform_rubric_controller::DISPLAY_EDIT_FROZEN,
 130              gradingform_rubric_controller::DISPLAY_PREVIEW,
 131              gradingform_rubric_controller::DISPLAY_PREVIEW_GRADED,
 132          ]);
 133          $levelsrowparams['role'] = $isradiogroup ? 'radiogroup' : 'list';
 134          $levelsrow = html_writer::tag('tr', $levelsstr, $levelsrowparams);
 135  
 136          $levelstableparams = [
 137              'id' => '{NAME}-criteria-{CRITERION-id}-levels-table',
 138              'role' => 'none',
 139          ];
 140          $levelsstrtable = html_writer::tag('table', $levelsrow, $levelstableparams);
 141          $levelsclass = 'levels';
 142          if (isset($criterion['error_levels'])) {
 143              $levelsclass .= ' error';
 144          }
 145          $criteriontemplate .= html_writer::tag('td', $levelsstrtable, array('class' => $levelsclass));
 146          if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) {
 147              $value = get_string('criterionaddlevel', 'gradingform_rubric');
 148              $button = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][addlevel]',
 149                  'id' => '{NAME}-criteria-{CRITERION-id}-levels-addlevel', 'value' => $value, 'class' => 'btn btn-secondary'));
 150              $criteriontemplate .= html_writer::tag('td', $button, array('class' => 'addlevel'));
 151          }
 152          $displayremark = ($options['enableremarks'] && ($mode != gradingform_rubric_controller::DISPLAY_VIEW || $options['showremarksstudent']));
 153          if ($displayremark) {
 154              $currentremark = '';
 155              if (isset($value['remark'])) {
 156                  $currentremark = $value['remark'];
 157              }
 158  
 159              // Label for criterion remark.
 160              $remarkinfo = new stdClass();
 161              $remarkinfo->description = s($criterion['description']);
 162              $remarkinfo->remark = $currentremark;
 163              $remarklabeltext = get_string('criterionremark', 'gradingform_rubric', $remarkinfo);
 164  
 165              if ($mode == gradingform_rubric_controller::DISPLAY_EVAL) {
 166                  // HTML parameters for remarks text area.
 167                  $remarkparams = array(
 168                      'name' => '{NAME}[criteria][{CRITERION-id}][remark]',
 169                      'id' => '{NAME}-criteria-{CRITERION-id}-remark',
 170                      'cols' => '10', 'rows' => '5',
 171                      'aria-label' => $remarklabeltext
 172                  );
 173                  $input = html_writer::tag('textarea', s($currentremark), $remarkparams);
 174                  $criteriontemplate .= html_writer::tag('td', $input, array('class' => 'remark'));
 175              } else if ($mode == gradingform_rubric_controller::DISPLAY_EVAL_FROZEN) {
 176                  $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][remark]', 'value' => $currentremark));
 177              }else if ($mode == gradingform_rubric_controller::DISPLAY_REVIEW || $mode == gradingform_rubric_controller::DISPLAY_VIEW) {
 178                  // HTML parameters for remarks cell.
 179                  $remarkparams = array(
 180                      'class' => 'remark',
 181                      'tabindex' => '0',
 182                      'id' => '{NAME}-criteria-{CRITERION-id}-remark',
 183                      'aria-label' => $remarklabeltext
 184                  );
 185                  $criteriontemplate .= html_writer::tag('td', s($currentremark), $remarkparams);
 186              }
 187          }
 188          $criteriontemplate .= html_writer::end_tag('tr'); // .criterion
 189  
 190          $criteriontemplate = str_replace('{NAME}', $elementname, $criteriontemplate);
 191          $criteriontemplate = str_replace('{CRITERION-id}', $criterion['id'], $criteriontemplate);
 192          return $criteriontemplate;
 193      }
 194  
 195      /**
 196       * This function returns html code for displaying one level of one criterion. Depending on $mode
 197       * it may be the code to edit rubric, to preview the rubric, to evaluate somebody or to review the evaluation.
 198       *
 199       * This function may be called from display_rubric() to display the whole rubric, or it can be
 200       * called by itself to return a template used by JavaScript to add new empty level to the
 201       * criterion during the design of rubric.
 202       * In this case it will use macros like {NAME}, {CRITERION-id}, {LEVEL-id}, etc.
 203       *
 204       * When overriding this function it is very important to remember that all elements of html
 205       * form (in edit or evaluate mode) must have the name $elementname.
 206       *
 207       * Also JavaScript relies on the class names of elements and when developer changes them
 208       * script might stop working.
 209       *
 210       * @param int $mode rubric display mode see {@link gradingform_rubric_controller}
 211       * @param array $options display options for this rubric, defaults are: {@link gradingform_rubric_controller::get_default_options()}
 212       * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
 213       * @param string|int $criterionid either id of the nesting criterion or a macro for template
 214       * @param array|null $level level data, also in view mode it might also have property $level['checked'] whether this level is checked
 215       * @return string
 216       */
 217      public function level_template($mode, $options, $elementname = '{NAME}', $criterionid = '{CRITERION-id}', $level = null) {
 218          // TODO MDL-31235 definition format
 219          if (!isset($level['id'])) {
 220              $level = array('id' => '{LEVEL-id}', 'definition' => '{LEVEL-definition}', 'score' => '{LEVEL-score}', 'class' => '{LEVEL-class}', 'checked' => false);
 221          } else {
 222              foreach (array('score', 'definition', 'class', 'checked', 'index') as $key) {
 223                  // set missing array elements to empty strings to avoid warnings
 224                  if (!array_key_exists($key, $level)) {
 225                      $level[$key] = '';
 226                  }
 227              }
 228          }
 229  
 230          // Get level index.
 231          $levelindex = isset($level['index']) ? $level['index'] : '{LEVEL-index}';
 232  
 233          // Template for one level within one criterion
 234          $tdattributes = array(
 235              'id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}',
 236              'class' => 'text-break level' . $level['class']
 237          );
 238          if (isset($level['tdwidth'])) {
 239              $tdattributes['style'] = "width: " . round($level['tdwidth']).'%;';
 240          }
 241  
 242          $leveltemplate = html_writer::start_tag('div', array('class' => 'level-wrapper'));
 243          if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) {
 244              $definitionparams = array(
 245                  'id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}-definition',
 246                  'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][definition]',
 247                  'aria-label' => get_string('leveldefinition', 'gradingform_rubric', $levelindex),
 248                  'cols' => '10', 'rows' => '4'
 249              );
 250              $definition = html_writer::tag('textarea', s($level['definition']), $definitionparams);
 251  
 252              $scoreparams = array(
 253                  'type' => 'text',
 254                  'id' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][score]',
 255                  'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][score]',
 256                  'aria-label' => get_string('scoreinputforlevel', 'gradingform_rubric', $levelindex),
 257                  'size' => '3',
 258                  'value' => $level['score']
 259              );
 260              $score = html_writer::empty_tag('input', $scoreparams);
 261          } else {
 262              if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) {
 263                  $leveltemplate .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][definition]', 'value' => $level['definition']));
 264                  $leveltemplate .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][score]', 'value' => $level['score']));
 265              }
 266              $definition = s($level['definition']);
 267              $score = $level['score'];
 268          }
 269          if ($mode == gradingform_rubric_controller::DISPLAY_EVAL) {
 270              $levelradioparams = array(
 271                  'type' => 'radio',
 272                  'id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}-definition',
 273                  'name' => '{NAME}[criteria][{CRITERION-id}][levelid]',
 274                  'value' => $level['id']
 275              );
 276              if ($level['checked']) {
 277                  $levelradioparams['checked'] = 'checked';
 278              }
 279              $input = html_writer::empty_tag('input', $levelradioparams);
 280              $leveltemplate .= html_writer::div($input, 'radio');
 281          }
 282          if ($mode == gradingform_rubric_controller::DISPLAY_EVAL_FROZEN && $level['checked']) {
 283              $leveltemplate .= html_writer::empty_tag('input',
 284                  array(
 285                      'type' => 'hidden',
 286                      'name' => '{NAME}[criteria][{CRITERION-id}][levelid]',
 287                      'value' => $level['id']
 288                  )
 289              );
 290          }
 291          $score = html_writer::tag('span', $score, array('id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}-score', 'class' => 'scorevalue'));
 292          $definitionclass = 'definition';
 293          if (isset($level['error_definition'])) {
 294              $definitionclass .= ' error';
 295          }
 296  
 297          if ($mode != gradingform_rubric_controller::DISPLAY_EDIT_FULL &&
 298              $mode != gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) {
 299  
 300              $tdattributes['tabindex'] = '0';
 301              $levelinfo = new stdClass();
 302              $levelinfo->definition = s($level['definition']);
 303              $levelinfo->score = $level['score'];
 304              $tdattributes['aria-label'] = get_string('level', 'gradingform_rubric', $levelinfo);
 305  
 306              if ($mode != gradingform_rubric_controller::DISPLAY_PREVIEW &&
 307                  $mode != gradingform_rubric_controller::DISPLAY_PREVIEW_GRADED) {
 308                  // Add role of radio button to level cell if not in edit and preview mode.
 309                  $tdattributes['role'] = 'radio';
 310                  if ($level['checked']) {
 311                      $tdattributes['aria-checked'] = 'true';
 312                  } else {
 313                      $tdattributes['aria-checked'] = 'false';
 314                  }
 315              } else {
 316                  $tdattributes['role'] = 'listitem';
 317              }
 318          } else {
 319              $tdattributes['role'] = 'listitem';
 320          }
 321  
 322          $leveltemplateparams = array(
 323              'id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}-definition-container'
 324          );
 325          $leveltemplate .= html_writer::div($definition, $definitionclass, $leveltemplateparams);
 326          $displayscore = true;
 327          if (!$options['showscoreteacher'] && in_array($mode, array(gradingform_rubric_controller::DISPLAY_EVAL, gradingform_rubric_controller::DISPLAY_EVAL_FROZEN, gradingform_rubric_controller::DISPLAY_REVIEW))) {
 328              $displayscore = false;
 329          }
 330          if (!$options['showscorestudent'] && in_array($mode, array(gradingform_rubric_controller::DISPLAY_VIEW, gradingform_rubric_controller::DISPLAY_PREVIEW_GRADED))) {
 331              $displayscore = false;
 332          }
 333          if ($displayscore) {
 334              $scoreclass = 'score d-inline';
 335              if (isset($level['error_score'])) {
 336                  $scoreclass .= ' error';
 337              }
 338              $leveltemplate .= html_writer::tag('div', get_string('scorepostfix', 'gradingform_rubric', $score), array('class' => $scoreclass));
 339          }
 340          if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) {
 341              $value = get_string('leveldelete', 'gradingform_rubric', $levelindex);
 342              $buttonparams = array(
 343                  'type' => 'submit',
 344                  'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][delete]',
 345                  'id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}-delete',
 346                  'value' => $value
 347              );
 348              $button = html_writer::empty_tag('input', $buttonparams);
 349              $leveltemplate .= html_writer::tag('div', $button, array('class' => 'delete'));
 350          }
 351          $leveltemplate .= html_writer::end_tag('div'); // .level-wrapper
 352  
 353          $leveltemplate = html_writer::tag('td', $leveltemplate, $tdattributes); // The .level cell.
 354  
 355          $leveltemplate = str_replace('{NAME}', $elementname, $leveltemplate);
 356          $leveltemplate = str_replace('{CRITERION-id}', $criterionid, $leveltemplate);
 357          $leveltemplate = str_replace('{LEVEL-id}', $level['id'], $leveltemplate);
 358          return $leveltemplate;
 359      }
 360  
 361      /**
 362       * This function returns html code for displaying rubric template (content before and after
 363       * criteria list). Depending on $mode it may be the code to edit rubric, to preview the rubric,
 364       * to evaluate somebody or to review the evaluation.
 365       *
 366       * This function is called from display_rubric() to display the whole rubric.
 367       *
 368       * When overriding this function it is very important to remember that all elements of html
 369       * form (in edit or evaluate mode) must have the name $elementname.
 370       *
 371       * Also JavaScript relies on the class names of elements and when developer changes them
 372       * script might stop working.
 373       *
 374       * @param int $mode rubric display mode see {@link gradingform_rubric_controller}
 375       * @param array $options display options for this rubric, defaults are: {@link gradingform_rubric_controller::get_default_options()}
 376       * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
 377       * @param string $criteriastr evaluated templates for this rubric's criteria
 378       * @return string
 379       */
 380      protected function rubric_template($mode, $options, $elementname, $criteriastr) {
 381          $classsuffix = ''; // CSS suffix for class of the main div. Depends on the mode
 382          switch ($mode) {
 383              case gradingform_rubric_controller::DISPLAY_EDIT_FULL:
 384                  $classsuffix = ' editor editable'; break;
 385              case gradingform_rubric_controller::DISPLAY_EDIT_FROZEN:
 386                  $classsuffix = ' editor frozen';  break;
 387              case gradingform_rubric_controller::DISPLAY_PREVIEW:
 388              case gradingform_rubric_controller::DISPLAY_PREVIEW_GRADED:
 389                  $classsuffix = ' editor preview';  break;
 390              case gradingform_rubric_controller::DISPLAY_EVAL:
 391                  $classsuffix = ' evaluate editable'; break;
 392              case gradingform_rubric_controller::DISPLAY_EVAL_FROZEN:
 393                  $classsuffix = ' evaluate frozen';  break;
 394              case gradingform_rubric_controller::DISPLAY_REVIEW:
 395                  $classsuffix = ' review';  break;
 396              case gradingform_rubric_controller::DISPLAY_VIEW:
 397                  $classsuffix = ' view';  break;
 398          }
 399  
 400          $rubrictemplate = html_writer::start_tag('div', array('id' => 'rubric-{NAME}', 'class' => 'clearfix gradingform_rubric'.$classsuffix));
 401  
 402          // Rubric table.
 403          $rubrictableparams = [
 404              'class' => 'criteria',
 405              'id' => '{NAME}-criteria',
 406          ];
 407          $caption = html_writer::tag('caption', get_string('rubric', 'gradingform_rubric'), ['class' => 'sr-only']);
 408          $rubrictable = html_writer::tag('table', $caption . $criteriastr, $rubrictableparams);
 409          $rubrictemplate .= $rubrictable;
 410          if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) {
 411              $value = get_string('addcriterion', 'gradingform_rubric');
 412              $criteriainputparams = array(
 413                  'type' => 'submit',
 414                  'name' => '{NAME}[criteria][addcriterion]',
 415                  'id' => '{NAME}-criteria-addcriterion',
 416                  'value' => $value
 417              );
 418              $input = html_writer::empty_tag('input', $criteriainputparams);
 419              $rubrictemplate .= html_writer::tag('div', $input, array('class' => 'addcriterion btn btn-secondary'));
 420          }
 421          $rubrictemplate .= $this->rubric_edit_options($mode, $options);
 422          $rubrictemplate .= html_writer::end_tag('div');
 423  
 424          return str_replace('{NAME}', $elementname, $rubrictemplate);
 425      }
 426  
 427      /**
 428       * Generates html template to view/edit the rubric options. Expression {NAME} is used in
 429       * template for the form element name
 430       *
 431       * @param int $mode rubric display mode see {@link gradingform_rubric_controller}
 432       * @param array $options display options for this rubric, defaults are: {@link gradingform_rubric_controller::get_default_options()}
 433       * @return string
 434       */
 435      protected function rubric_edit_options($mode, $options) {
 436          if ($mode != gradingform_rubric_controller::DISPLAY_EDIT_FULL
 437                  && $mode != gradingform_rubric_controller::DISPLAY_EDIT_FROZEN
 438                  && $mode != gradingform_rubric_controller::DISPLAY_PREVIEW) {
 439              // Options are displayed only for people who can manage
 440              return;
 441          }
 442          $html = html_writer::start_tag('div', array('class' => 'options'));
 443          $html .= html_writer::tag('div', get_string('rubricoptions', 'gradingform_rubric'), array('class' => 'optionsheading'));
 444          $attrs = array('type' => 'hidden', 'name' => '{NAME}[options][optionsset]', 'value' => 1);
 445          foreach ($options as $option => $value) {
 446              $html .= html_writer::start_tag('div', array('class' => 'option '.$option));
 447              $attrs = array('name' => '{NAME}[options]['.$option.']', 'id' => '{NAME}-options-'.$option);
 448              switch ($option) {
 449                  case 'sortlevelsasc':
 450                      // Display option as dropdown
 451                      $html .= html_writer::label(get_string($option, 'gradingform_rubric'), $attrs['id'], false);
 452                      $value = (int)(!!$value); // make sure $value is either 0 or 1
 453                      if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) {
 454                          $selectoptions = array(0 => get_string($option.'0', 'gradingform_rubric'), 1 => get_string($option.'1', 'gradingform_rubric'));
 455                          $valuestr = html_writer::select($selectoptions, $attrs['name'], $value, false, array('id' => $attrs['id']));
 456                          $html .= html_writer::tag('span', $valuestr, array('class' => 'value'));
 457                      } else {
 458                          $html .= html_writer::tag('span', get_string($option.$value, 'gradingform_rubric'), array('class' => 'value'));
 459                          if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) {
 460                              $html .= html_writer::empty_tag('input', $attrs + array('type' => 'hidden', 'value' => $value));
 461                          }
 462                      }
 463                      break;
 464                  default:
 465                      if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN && $value) {
 466                          // Id should be different then the actual input added later.
 467                          $attrs['id'] .= '_hidden';
 468                          $html .= html_writer::empty_tag('input', $attrs + array('type' => 'hidden', 'value' => $value));
 469                      }
 470                      // Display option as checkbox
 471                      $attrs['type'] = 'checkbox';
 472                      $attrs['value'] = 1;
 473                      if ($value) {
 474                          $attrs['checked'] = 'checked';
 475                      }
 476                      if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN || $mode == gradingform_rubric_controller::DISPLAY_PREVIEW) {
 477                          $attrs['disabled'] = 'disabled';
 478                          unset($attrs['name']);
 479                          // Id should be different then the actual input added later.
 480                          $attrs['id'] .= '_disabled';
 481                      }
 482                      $html .= html_writer::empty_tag('input', $attrs);
 483                      $html .= html_writer::tag('label', get_string($option, 'gradingform_rubric'), array('for' => $attrs['id']));
 484                      break;
 485              }
 486              if (get_string_manager()->string_exists($option.'_help', 'gradingform_rubric')) {
 487                  $html .= $this->help_icon($option, 'gradingform_rubric');
 488              }
 489              $html .= html_writer::end_tag('div'); // .option
 490          }
 491          $html .= html_writer::end_tag('div'); // .options
 492          return $html;
 493      }
 494  
 495      /**
 496       * This function returns html code for displaying rubric. Depending on $mode it may be the code
 497       * to edit rubric, to preview the rubric, to evaluate somebody or to review the evaluation.
 498       *
 499       * It is very unlikely that this function needs to be overriden by theme. It does not produce
 500       * any html code, it just prepares data about rubric design and evaluation, adds the CSS
 501       * class to elements and calls the functions level_template, criterion_template and
 502       * rubric_template
 503       *
 504       * @param array $criteria data about the rubric design
 505       * @param array $options display options for this rubric, defaults are: {@link gradingform_rubric_controller::get_default_options()}
 506       * @param int $mode rubric display mode, see {@link gradingform_rubric_controller}
 507       * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
 508       * @param array $values evaluation result
 509       * @return string
 510       */
 511      public function display_rubric($criteria, $options, $mode, $elementname = null, $values = null) {
 512          $criteriastr = '';
 513          $cnt = 0;
 514          foreach ($criteria as $id => $criterion) {
 515              $criterion['class'] = $this->get_css_class_suffix($cnt++, sizeof($criteria) -1);
 516              $criterion['id'] = $id;
 517              $levelsstr = '';
 518              $levelcnt = 0;
 519              if (isset($values['criteria'][$id])) {
 520                  $criterionvalue = $values['criteria'][$id];
 521              } else {
 522                  $criterionvalue = null;
 523              }
 524              $index = 1;
 525              foreach ($criterion['levels'] as $levelid => $level) {
 526                  $level['id'] = $levelid;
 527                  $level['class'] = $this->get_css_class_suffix($levelcnt++, sizeof($criterion['levels']) -1);
 528                  $level['checked'] = (isset($criterionvalue['levelid']) && ((int)$criterionvalue['levelid'] === $levelid));
 529                  if ($level['checked'] && ($mode == gradingform_rubric_controller::DISPLAY_EVAL_FROZEN || $mode == gradingform_rubric_controller::DISPLAY_REVIEW || $mode == gradingform_rubric_controller::DISPLAY_VIEW)) {
 530                      $level['class'] .= ' checked';
 531                      //in mode DISPLAY_EVAL the class 'checked' will be added by JS if it is enabled. If JS is not enabled, the 'checked' class will only confuse
 532                  }
 533                  if (isset($criterionvalue['savedlevelid']) && ((int)$criterionvalue['savedlevelid'] === $levelid)) {
 534                      $level['class'] .= ' currentchecked';
 535                  }
 536                  $level['tdwidth'] = 100/count($criterion['levels']);
 537                  $level['index'] = $index;
 538                  $levelsstr .= $this->level_template($mode, $options, $elementname, $id, $level);
 539                  $index++;
 540              }
 541              $criteriastr .= $this->criterion_template($mode, $options, $elementname, $criterion, $levelsstr, $criterionvalue);
 542          }
 543          return $this->rubric_template($mode, $options, $elementname, $criteriastr);
 544      }
 545  
 546      /**
 547       * Help function to return CSS class names for element (first/last/even/odd) with leading space
 548       *
 549       * @param int $idx index of this element in the row/column
 550       * @param int $maxidx maximum index of the element in the row/column
 551       * @return string
 552       */
 553      protected function get_css_class_suffix($idx, $maxidx) {
 554          $class = '';
 555          if ($idx == 0) {
 556              $class .= ' first';
 557          }
 558          if ($idx == $maxidx) {
 559              $class .= ' last';
 560          }
 561          if ($idx%2) {
 562              $class .= ' odd';
 563          } else {
 564              $class .= ' even';
 565          }
 566          return $class;
 567      }
 568  
 569      /**
 570       * Displays for the student the list of instances or default content if no instances found
 571       *
 572       * @param array $instances array of objects of type gradingform_rubric_instance
 573       * @param string $defaultcontent default string that would be displayed without advanced grading
 574       * @param boolean $cangrade whether current user has capability to grade in this context
 575       * @return string
 576       */
 577      public function display_instances($instances, $defaultcontent, $cangrade) {
 578          $return = '';
 579          if (sizeof($instances)) {
 580              $return .= html_writer::start_tag('div', array('class' => 'advancedgrade'));
 581              $idx = 0;
 582              foreach ($instances as $instance) {
 583                  $return .= $this->display_instance($instance, $idx++, $cangrade);
 584              }
 585              $return .= html_writer::end_tag('div');
 586          }
 587          return $return. $defaultcontent;
 588      }
 589  
 590      /**
 591       * Displays one grading instance
 592       *
 593       * @param gradingform_rubric_instance $instance
 594       * @param int $idx unique number of instance on page
 595       * @param bool $cangrade whether current user has capability to grade in this context
 596       */
 597      public function display_instance(gradingform_rubric_instance $instance, $idx, $cangrade) {
 598          $criteria = $instance->get_controller()->get_definition()->rubric_criteria;
 599          $options = $instance->get_controller()->get_options();
 600          $values = $instance->get_rubric_filling();
 601          if ($cangrade) {
 602              $mode = gradingform_rubric_controller::DISPLAY_REVIEW;
 603              $showdescription = $options['showdescriptionteacher'];
 604          } else {
 605              $mode = gradingform_rubric_controller::DISPLAY_VIEW;
 606              $showdescription = $options['showdescriptionstudent'];
 607          }
 608          $output = '';
 609          if ($showdescription) {
 610              $output .= $this->box($instance->get_controller()->get_formatted_description(), 'gradingform_rubric-description');
 611          }
 612          $output .= $this->display_rubric($criteria, $options, $mode, 'rubric'.$idx, $values);
 613          return $output;
 614      }
 615  
 616      /**
 617       * Displays confirmation that students require re-grading
 618       *
 619       * @param string $elementname
 620       * @param int $changelevel
 621       * @param string $value
 622       * @return string
 623       */
 624      public function display_regrade_confirmation($elementname, $changelevel, $value) {
 625          $html = html_writer::start_tag('div', array('class' => 'gradingform_rubric-regrade', 'role' => 'alert'));
 626          if ($changelevel<=2) {
 627              $html .= html_writer::label(get_string('regrademessage1', 'gradingform_rubric'), 'menu' . $elementname . 'regrade');
 628              $selectoptions = array(
 629                  0 => get_string('regradeoption0', 'gradingform_rubric'),
 630                  1 => get_string('regradeoption1', 'gradingform_rubric')
 631              );
 632              $html .= html_writer::select($selectoptions, $elementname.'[regrade]', $value, false);
 633          } else {
 634              $html .= get_string('regrademessage5', 'gradingform_rubric');
 635              $html .= html_writer::empty_tag('input', array('name' => $elementname.'[regrade]', 'value' => 1, 'type' => 'hidden'));
 636          }
 637          $html .= html_writer::end_tag('div');
 638          return $html;
 639      }
 640  
 641      /**
 642       * Generates and returns HTML code to display information box about how rubric score is converted to the grade
 643       *
 644       * @param array $scores
 645       * @return string
 646       */
 647      public function display_rubric_mapping_explained($scores) {
 648          $html = '';
 649          if (!$scores) {
 650              return $html;
 651          }
 652          if ($scores['minscore'] <> 0) {
 653              $html .= $this->output->notification(get_string('zerolevelsabsent', 'gradingform_rubric'), 'error');
 654          }
 655          $html .= $this->output->notification(get_string('rubricmappingexplained', 'gradingform_rubric', (object)$scores), 'info');
 656          return $html;
 657      }
 658  }