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] [Versions 39 and 310]

   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   * Moodle renderer used to display special elements of the lesson module
  20   *
  21   * @package   mod_choice
  22   * @copyright 2010 Rossiani Wijaya
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   **/
  25  define ('DISPLAY_HORIZONTAL_LAYOUT', 0);
  26  define ('DISPLAY_VERTICAL_LAYOUT', 1);
  27  
  28  class mod_choice_renderer extends plugin_renderer_base {
  29  
  30      /**
  31       * Returns HTML to display choices of option
  32       * @param object $options
  33       * @param int  $coursemoduleid
  34       * @param bool $vertical
  35       * @return string
  36       */
  37      public function display_options($options, $coursemoduleid, $vertical = false, $multiple = false) {
  38          $layoutclass = 'horizontal';
  39          if ($vertical) {
  40              $layoutclass = 'vertical';
  41          }
  42          $target = new moodle_url('/mod/choice/view.php');
  43          $attributes = array('method'=>'POST', 'action'=>$target, 'class'=> $layoutclass);
  44          $disabled = empty($options['previewonly']) ? array() : array('disabled' => 'disabled');
  45  
  46          $html = html_writer::start_tag('form', $attributes);
  47          $html .= html_writer::start_tag('ul', array('class' => 'choices list-unstyled unstyled'));
  48  
  49          $availableoption = count($options['options']);
  50          $choicecount = 0;
  51          foreach ($options['options'] as $option) {
  52              $choicecount++;
  53              $html .= html_writer::start_tag('li', array('class' => 'option mr-3'));
  54              if ($multiple) {
  55                  $option->attributes->name = 'answer[]';
  56                  $option->attributes->type = 'checkbox';
  57              } else {
  58                  $option->attributes->name = 'answer';
  59                  $option->attributes->type = 'radio';
  60              }
  61              $option->attributes->id = 'choice_'.$choicecount;
  62              $option->attributes->class = 'mx-1';
  63  
  64              $labeltext = $option->text;
  65              if (!empty($option->attributes->disabled)) {
  66                  $labeltext .= ' ' . get_string('full', 'choice');
  67                  $availableoption--;
  68              }
  69  
  70              if (!empty($options['limitanswers']) && !empty($options['showavailable'])) {
  71                  $labeltext .= html_writer::empty_tag('br');
  72                  $labeltext .= get_string("responsesa", "choice", $option->countanswers);
  73                  $labeltext .= html_writer::empty_tag('br');
  74                  $labeltext .= get_string("limita", "choice", $option->maxanswers);
  75              }
  76  
  77              $html .= html_writer::empty_tag('input', (array)$option->attributes + $disabled);
  78              $html .= html_writer::tag('label', $labeltext, array('for'=>$option->attributes->id));
  79              $html .= html_writer::end_tag('li');
  80          }
  81          $html .= html_writer::tag('li','', array('class'=>'clearfloat'));
  82          $html .= html_writer::end_tag('ul');
  83          $html .= html_writer::tag('div', '', array('class'=>'clearfloat'));
  84          $html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=>sesskey()));
  85          $html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'action', 'value'=>'makechoice'));
  86          $html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'id', 'value'=>$coursemoduleid));
  87  
  88          if (empty($options['previewonly'])) {
  89              if (!empty($options['hascapability']) && ($options['hascapability'])) {
  90                  if ($availableoption < 1) {
  91                      $html .= html_writer::tag('label', get_string('choicefull', 'choice'));
  92                  } else {
  93                      $html .= html_writer::empty_tag('input', array(
  94                          'type' => 'submit',
  95                          'value' => get_string('savemychoice', 'choice'),
  96                          'class' => 'btn btn-primary'
  97                      ));
  98                  }
  99  
 100                  if (!empty($options['allowupdate']) && ($options['allowupdate'])) {
 101                      $url = new moodle_url('view.php',
 102                              array('id' => $coursemoduleid, 'action' => 'delchoice', 'sesskey' => sesskey()));
 103                      $html .= html_writer::link($url, get_string('removemychoice', 'choice'), array('class' => 'ml-1'));
 104                  }
 105              } else {
 106                  $html .= html_writer::tag('label', get_string('havetologin', 'choice'));
 107              }
 108          }
 109  
 110          $html .= html_writer::end_tag('ul');
 111          $html .= html_writer::end_tag('form');
 112  
 113          return $html;
 114      }
 115  
 116      /**
 117       * Returns HTML to display choices result
 118       * @param object $choices
 119       * @param bool $forcepublish
 120       * @return string
 121       */
 122      public function display_result($choices, $forcepublish = false) {
 123          if (empty($forcepublish)) { //allow the publish setting to be overridden
 124              $forcepublish = $choices->publish;
 125          }
 126  
 127          $displaylayout = $choices->display;
 128  
 129          if ($forcepublish) {  //CHOICE_PUBLISH_NAMES
 130              return $this->display_publish_name_vertical($choices);
 131          } else {
 132              return $this->display_publish_anonymous($choices, $displaylayout);
 133          }
 134      }
 135  
 136      /**
 137       * Returns HTML to display choices result
 138       * @param object $choices
 139       * @return string
 140       */
 141      public function display_publish_name_vertical($choices) {
 142          $html ='';
 143          $html .= html_writer::tag('h3',format_string(get_string("responses", "choice")));
 144  
 145          $attributes = array('method'=>'POST');
 146          $attributes['action'] = new moodle_url($this->page->url);
 147          $attributes['id'] = 'attemptsform';
 148  
 149          if ($choices->viewresponsecapability) {
 150              $html .= html_writer::start_tag('form', $attributes);
 151              $html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'id', 'value'=> $choices->coursemoduleid));
 152              $html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=> sesskey()));
 153              $html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'mode', 'value'=>'overview'));
 154          }
 155  
 156          $table = new html_table();
 157          $table->cellpadding = 0;
 158          $table->cellspacing = 0;
 159          $table->attributes['class'] = 'results names table table-bordered';
 160          $table->tablealign = 'center';
 161          $table->summary = get_string('responsesto', 'choice', format_string($choices->name));
 162          $table->data = array();
 163  
 164          $count = 0;
 165          ksort($choices->options);
 166  
 167          $columns = array();
 168          $celldefault = new html_table_cell();
 169          $celldefault->attributes['class'] = 'data';
 170  
 171          // This extra cell is needed in order to support accessibility for screenreader. MDL-30816
 172          $accessiblecell = new html_table_cell();
 173          $accessiblecell->scope = 'row';
 174          $accessiblecell->text = get_string('choiceoptions', 'choice');
 175          $columns['options'][] = $accessiblecell;
 176  
 177          $usernumberheader = clone($celldefault);
 178          $usernumberheader->header = true;
 179          $usernumberheader->attributes['class'] = 'header data';
 180          $usernumberheader->text = get_string('numberofuser', 'choice');
 181          $columns['usernumber'][] = $usernumberheader;
 182  
 183          $optionsnames = [];
 184          foreach ($choices->options as $optionid => $options) {
 185              $celloption = clone($celldefault);
 186              $cellusernumber = clone($celldefault);
 187  
 188              if ($choices->showunanswered && $optionid == 0) {
 189                  $headertitle = get_string('notanswered', 'choice');
 190              } else if ($optionid > 0) {
 191                  $headertitle = format_string($choices->options[$optionid]->text);
 192                  if (!empty($choices->options[$optionid]->user) && count($choices->options[$optionid]->user) > 0) {
 193                      if ((count($choices->options[$optionid]->user)) == ($choices->options[$optionid]->maxanswer)) {
 194                          $headertitle .= ' ' . get_string('full', 'choice');
 195                      }
 196                  }
 197              }
 198              $celltext = $headertitle;
 199  
 200              // Render select/deselect all checkbox for this option.
 201              if ($choices->viewresponsecapability && $choices->deleterepsonsecapability) {
 202  
 203                  // Build the select/deselect all for this option.
 204                  $selectallid = 'select-response-option-' . $optionid;
 205                  $togglegroup = 'responses response-option-' . $optionid;
 206                  $selectalltext = get_string('selectalloption', 'choice', $headertitle);
 207                  $deselectalltext = get_string('deselectalloption', 'choice', $headertitle);
 208                  $mastercheckbox = new \core\output\checkbox_toggleall($togglegroup, true, [
 209                      'id' => $selectallid,
 210                      'name' => $selectallid,
 211                      'value' => 1,
 212                      'selectall' => $selectalltext,
 213                      'deselectall' => $deselectalltext,
 214                      'label' => $selectalltext,
 215                      'labelclasses' => 'accesshide',
 216                  ]);
 217  
 218                  $celltext .= html_writer::div($this->output->render($mastercheckbox));
 219              }
 220              $numberofuser = 0;
 221              if (!empty($options->user) && count($options->user) > 0) {
 222                  $numberofuser = count($options->user);
 223              }
 224              if (($choices->limitanswers) && ($choices->showavailable)) {
 225                  $numberofuser .= html_writer::empty_tag('br');
 226                  $numberofuser .= get_string("limita", "choice", $options->maxanswer);
 227              }
 228              $celloption->text = html_writer::div($celltext, 'text-center');
 229              $optionsnames[$optionid] = $celltext;
 230              $cellusernumber->text = html_writer::div($numberofuser, 'text-center');
 231  
 232              $columns['options'][] = $celloption;
 233              $columns['usernumber'][] = $cellusernumber;
 234          }
 235  
 236          $table->head = $columns['options'];
 237          $table->data[] = new html_table_row($columns['usernumber']);
 238  
 239          $columns = array();
 240  
 241          // This extra cell is needed in order to support accessibility for screenreader. MDL-30816
 242          $accessiblecell = new html_table_cell();
 243          $accessiblecell->text = get_string('userchoosethisoption', 'choice');
 244          $accessiblecell->header = true;
 245          $accessiblecell->scope = 'row';
 246          $accessiblecell->attributes['class'] = 'header data';
 247          $columns[] = $accessiblecell;
 248  
 249          foreach ($choices->options as $optionid => $options) {
 250              $cell = new html_table_cell();
 251              $cell->attributes['class'] = 'data';
 252  
 253              if ($choices->showunanswered || $optionid > 0) {
 254                  if (!empty($options->user)) {
 255                      $optionusers = '';
 256                      foreach ($options->user as $user) {
 257                          $data = '';
 258                          if (empty($user->imagealt)) {
 259                              $user->imagealt = '';
 260                          }
 261  
 262                          $userfullname = fullname($user, $choices->fullnamecapability);
 263                          $checkbox = '';
 264                          if ($choices->viewresponsecapability && $choices->deleterepsonsecapability) {
 265                              $checkboxid = 'attempt-user' . $user->id . '-option' . $optionid;
 266                              if ($optionid > 0) {
 267                                  $checkboxname = 'attemptid[]';
 268                                  $checkboxvalue = $user->answerid;
 269                              } else {
 270                                  $checkboxname = 'userid[]';
 271                                  $checkboxvalue = $user->id;
 272                              }
 273  
 274                              $togglegroup = 'responses response-option-' . $optionid;
 275                              $slavecheckbox = new \core\output\checkbox_toggleall($togglegroup, false, [
 276                                  'id' => $checkboxid,
 277                                  'name' => $checkboxname,
 278                                  'classes' => 'mr-1',
 279                                  'value' => $checkboxvalue,
 280                                  'label' => $userfullname . ' ' . $options->text,
 281                                  'labelclasses' => 'accesshide',
 282                              ]);
 283                              $checkbox = $this->output->render($slavecheckbox);
 284                          }
 285                          $userimage = $this->output->user_picture($user, array('courseid' => $choices->courseid, 'link' => false));
 286                          $profileurl = new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $choices->courseid));
 287                          $profilelink = html_writer::link($profileurl, $userimage . $userfullname);
 288                          $data .= html_writer::div($checkbox . $profilelink, 'mb-1');
 289  
 290                          $optionusers .= $data;
 291                      }
 292                      $cell->text = $optionusers;
 293                  }
 294              }
 295              $columns[] = $cell;
 296              $count++;
 297          }
 298          $row = new html_table_row($columns);
 299          $table->data[] = $row;
 300  
 301          $html .= html_writer::tag('div', html_writer::table($table), array('class'=>'response'));
 302  
 303          $actiondata = '';
 304          if ($choices->viewresponsecapability && $choices->deleterepsonsecapability) {
 305              // Build the select/deselect all for all of options.
 306              $selectallid = 'select-all-responses';
 307              $togglegroup = 'responses';
 308              $selectallcheckbox = new \core\output\checkbox_toggleall($togglegroup, true, [
 309                  'id' => $selectallid,
 310                  'name' => $selectallid,
 311                  'value' => 1,
 312                  'label' => get_string('selectall'),
 313                  'classes' => 'btn-secondary mr-1'
 314              ], true);
 315              $actiondata .= $this->output->render($selectallcheckbox);
 316  
 317              $actionurl = new moodle_url($this->page->url,
 318                      ['sesskey' => sesskey(), 'action' => 'delete_confirmation()']);
 319              $actionoptions = array('delete' => get_string('delete'));
 320              foreach ($choices->options as $optionid => $option) {
 321                  if ($optionid > 0) {
 322                      $actionoptions['choose_'.$optionid] = get_string('chooseoption', 'choice', $option->text);
 323                  }
 324              }
 325              $selectattributes = [
 326                  'data-action' => 'toggle',
 327                  'data-togglegroup' => 'responses',
 328                  'data-toggle' => 'action',
 329              ];
 330              $selectnothing = ['' => get_string('chooseaction', 'choice')];
 331              $select = new single_select($actionurl, 'action', $actionoptions, null, $selectnothing, 'attemptsform');
 332              $select->set_label(get_string('withselected', 'choice'));
 333              $select->disabled = true;
 334              $select->attributes = $selectattributes;
 335  
 336              $actiondata .= $this->output->render($select);
 337          }
 338          $html .= html_writer::tag('div', $actiondata, array('class'=>'responseaction'));
 339  
 340          if ($choices->viewresponsecapability) {
 341              $html .= html_writer::end_tag('form');
 342          }
 343  
 344          return $html;
 345      }
 346  
 347  
 348      /**
 349       * Returns HTML to display choices result
 350       * @deprecated since 3.2
 351       * @param object $choices
 352       * @return string
 353       */
 354      public function display_publish_anonymous_horizontal($choices) {
 355          debugging(__FUNCTION__.'() is deprecated. Please use mod_choice_renderer::display_publish_anonymous() instead.',
 356                  DEBUG_DEVELOPER);
 357          return $this->display_publish_anonymous($choices, CHOICE_DISPLAY_VERTICAL);
 358      }
 359  
 360      /**
 361       * Returns HTML to display choices result
 362       * @deprecated since 3.2
 363       * @param object $choices
 364       * @return string
 365       */
 366      public function display_publish_anonymous_vertical($choices) {
 367          debugging(__FUNCTION__.'() is deprecated. Please use mod_choice_renderer::display_publish_anonymous() instead.',
 368                  DEBUG_DEVELOPER);
 369          return $this->display_publish_anonymous($choices, CHOICE_DISPLAY_HORIZONTAL);
 370      }
 371  
 372      /**
 373       * Generate the choice result chart.
 374       *
 375       * Can be displayed either in the vertical or horizontal position.
 376       *
 377       * @param stdClass $choices Choices responses object.
 378       * @param int $displaylayout The constants DISPLAY_HORIZONTAL_LAYOUT or DISPLAY_VERTICAL_LAYOUT.
 379       * @return string the rendered chart.
 380       */
 381      public function display_publish_anonymous($choices, $displaylayout) {
 382          $count = 0;
 383          $data = [];
 384          $numberofuser = 0;
 385          $percentageamount = 0;
 386          foreach ($choices->options as $optionid => $option) {
 387              if (!empty($option->user)) {
 388                  $numberofuser = count($option->user);
 389              }
 390              if($choices->numberofuser > 0) {
 391                  $percentageamount = ((float)$numberofuser / (float)$choices->numberofuser) * 100.0;
 392              }
 393              $data['labels'][$count] = $option->text;
 394              $data['series'][$count] = $numberofuser;
 395              $data['series_labels'][$count] = $numberofuser . ' (' . format_float($percentageamount, 1) . '%)';
 396              $count++;
 397              $numberofuser = 0;
 398          }
 399  
 400          $chart = new \core\chart_bar();
 401          if ($displaylayout == DISPLAY_HORIZONTAL_LAYOUT) {
 402              $chart->set_horizontal(true);
 403          }
 404          $series = new \core\chart_series(format_string(get_string("responses", "choice")), $data['series']);
 405          $series->set_labels($data['series_labels']);
 406          $chart->add_series($series);
 407          $chart->set_labels($data['labels']);
 408          $yaxis = $chart->get_yaxis(0, true);
 409          $yaxis->set_stepsize(max(1, round(max($data['series']) / 10)));
 410          return $this->output->render($chart);
 411      }
 412  }
 413