Search moodle.org's
Developer Documentation

See Release Notes

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

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace tool_brickfield\output\printable;
  18  
  19  use tool_brickfield\local\tool\bfpdf;
  20  use core\chart_bar;
  21  use core\chart_pie;
  22  use core\chart_series;
  23  use tool_brickfield\accessibility;
  24  use tool_brickfield\area_base;
  25  use tool_brickfield\local\tool\filter;
  26  use tool_brickfield\manager;
  27  
  28  /**
  29   * tool_brickfield/printable renderer
  30   *
  31   * @package    tool_brickfield
  32   * @copyright  2020 onward: Brickfield Education Labs, https://www.brickfield.ie
  33   * @author     Mike Churchward
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class renderer extends \tool_brickfield\output\renderer {
  37      /**
  38       * Render the page containing the Printable report.
  39       *
  40       * @param \stdClass $data Report data.
  41       * @param filter $filter Display filters.
  42       * @return String HTML showing charts.
  43       * @throws \coding_exception
  44       * @throws \dml_exception
  45       * @throws \moodle_exception
  46       */
  47      public function display(\stdClass $data, filter $filter): string {
  48          $css = '';
  49  
  50          // Page data.
  51          $out = '';
  52  
  53          if (empty($filter->target)) {
  54              $linkname = get_string('printable:downloadpdf', 'tool_brickfield');
  55              $link = new \moodle_url(
  56                  accessibility::get_plugin_url(),
  57                  [
  58                      'tab' => 'printable',
  59                      'courseid' => $filter->courseid,
  60                      'target' => 'pdf',
  61                  ]
  62              );
  63              $htmlicon = new \pix_icon('t/print', $linkname);
  64              $class = 'tool_brickfield_floatprinticon';
  65              $printlink = $this->action_link($link, $linkname, null, ['class' => $class, 'title' => $linkname], $htmlicon);
  66          }
  67  
  68          $out .= \html_writer::tag('h3', accessibility::get_title($filter, $data->countdata));
  69          $out .= !empty($printlink) ? $printlink : '';
  70  
  71          $div1 = \html_writer::div($this->pix_icon('f/award',
  72                  get_string('totalactivities', manager::PLUGINNAME), manager::PLUGINNAME).
  73              get_string('totalactivitiescount', manager::PLUGINNAME, $data->combodata['total']), '',
  74              ['class' => 'col-sm-3'.$css]);
  75          $div2 = \html_writer::div($this->pix_icon('f/done2',
  76                  get_string('passed', manager::PLUGINNAME), manager::PLUGINNAME).
  77              get_string('passedcount', manager::PLUGINNAME, $data->combodata['passed']), '',
  78              ['class' => 'col-sm-3'.$css]);
  79          $div3 = \html_writer::div($this->pix_icon('f/error',
  80                  get_string('failed', manager::PLUGINNAME), manager::PLUGINNAME).
  81              get_string('failedcount', manager::PLUGINNAME, $data->combodata['failed']), '',
  82              ['class' => 'col-sm-3'.$css]);
  83          $out .= \html_writer::div($div1.$div2.$div3, '', ['id' => 'rowa', 'class' => 'row h4']);
  84  
  85          $out .= \html_writer::div('&nbsp;'); // Padding row.
  86          $str1 = \html_writer::tag('h4', get_string('toperrors', manager::PLUGINNAME));
  87  
  88          $table = new \html_table();
  89          $table->head  = [
  90              get_string('tblcheck', manager::PLUGINNAME),
  91              get_string('count', manager::PLUGINNAME),
  92          ];
  93          $table->size = ['80%', '20%'];
  94          $table->align = ['left', 'center'];
  95          $data->checkcountdata = !empty($data->checkcountdata) ? $data->checkcountdata : [];
  96          foreach ($data->checkcountdata as $key => $value) {
  97              if ($value->checkcount > 0) {
  98                  $table->data[] = [$value->checkname, $value->checkcount];
  99              }
 100          }
 101  
 102          if (count($data->checkcountdata) > 0) {
 103              $str1 .= \html_writer::table($table, true);
 104          } else {
 105              $str1 .= \html_writer::tag('p', get_string('noerrorsfound', manager::PLUGINNAME));
 106          }
 107          $out .= \html_writer::start_div('row', ['id' => 'row2']);
 108          $out .= \html_writer::div($str1, '', ['class' => 'col-sm-4']);
 109  
 110          $str2 = \html_writer::tag('h4', get_string('toptargets', manager::PLUGINNAME));
 111          $table = new \html_table();
 112          $table->head  = [
 113              get_string('tbltarget', manager::PLUGINNAME),
 114              get_string('count', manager::PLUGINNAME),
 115          ];
 116          $table->size = ['80%', '20%'];
 117          $table->align = ['left', 'center'];
 118          $data->toptargetdata = !empty($data->toptargetdata) ? $data->toptargetdata : [];
 119          $table->data = $data->toptargetdata;
 120          if (count($data->toptargetdata) > 0) {
 121              $str2 .= \html_writer::table($table, true);
 122          } else {
 123              $str2 .= \html_writer::tag('p', get_string('noerrorsfound', manager::PLUGINNAME));
 124          }
 125          $out .= \html_writer::div($str2, '', ['class' => 'col-sm-4']);
 126  
 127          $str3 = \html_writer::tag('h4', get_string('taberrors', manager::PLUGINNAME));
 128          $table = new \html_table();
 129          $table->head  = [
 130              get_string('checktype', manager::PLUGINNAME),
 131              get_string('count', manager::PLUGINNAME),
 132          ];
 133          $table->size = ['80%', '20%'];
 134          $table->align = ['left', 'center'];
 135          foreach ($data->groupdata as $key => $group) {
 136              $checkgroup = area_base::checkgroup_name($key);
 137              $tmplabel = get_string('checktype:' . $checkgroup, manager::PLUGINNAME);
 138              $icon = $this->pix_icon('f/' . $checkgroup, $tmplabel, manager::PLUGINNAME);
 139              $table->data[] = [get_string('checktype:' . $checkgroup, manager::PLUGINNAME), $group->errorinstances];
 140          }
 141          $str3 .= \html_writer::table($table, true);
 142          $out .= \html_writer::div($str3, '', ['class' => 'col-sm-4']);
 143  
 144          $out .= \html_writer::end_div(); // End row2.
 145  
 146          $out .= \html_writer::start_div('row', ['id' => 'row3']);
 147  
 148          foreach ($data->combotardata as $key => &$combotar) {
 149              $combotar['passed'] = $combotar['total'] - $combotar['failed'];
 150              $combos[] = $combotar['total'] - $combotar['failed'];
 151              $combosf[] = $combotar['failed'];
 152          }
 153  
 154          $str4 = \html_writer::tag('h4', get_string('targetratio', manager::PLUGINNAME));
 155          $table = new \html_table();
 156          $table->head  = [
 157              get_string('tbltarget', manager::PLUGINNAME),
 158              get_string('passed', manager::PLUGINNAME),
 159              get_string('failed', manager::PLUGINNAME),
 160              get_string('total')
 161              ];
 162  
 163          foreach ($data->combotardata as $tar => $tarvalue) {
 164              $table->data[] = [$data->tarlabels[$tar],
 165                  $tarvalue['passed'], $tarvalue['failed'], $tarvalue['total']];
 166          }
 167  
 168          $table->size = ['40%', '20%', '20%', '20%'];
 169          $table->align = ['left', 'center', 'center', 'center'];
 170          $str4 .= \html_writer::table($table, true);
 171          $out .= \html_writer::div($str4, '', ['class' => 'col-sm-4']);
 172  
 173          $str5 = \html_writer::tag('h4', get_string('titleerrorscount', manager::PLUGINNAME, $data->errordetailscount));
 174          $table = new \html_table();
 175          $table->head  = [
 176              get_string('tbltarget', manager::PLUGINNAME),
 177              get_string('tblcheck', manager::PLUGINNAME),
 178              get_string('tblline', manager::PLUGINNAME),
 179              get_string('tblhtmlcode', manager::PLUGINNAME)
 180              ];
 181          $data->errordata = !empty($data->errordata) ? $data->errordata : [];
 182          foreach ($data->errordata as $err) {
 183              $err->htmlcode = htmlentities($err->htmlcode);
 184              $row = [$data->tarlabels[$err->component], $err->shortname, $err->errline, $err->htmlcode];
 185              $table->data[] = $row;
 186          }
 187  
 188          $table->size = ['10%', '25%', '5%', '60%'];
 189          if (count($data->errordata) > 0) {
 190              $str5 .= \html_writer::table($table, true);
 191          } else {
 192              $str5 .= \html_writer::tag('p', get_string('noerrorsfound', manager::PLUGINNAME));
 193          }
 194          $out .= \html_writer::div($str5, '', ['class' => 'col-sm-8']);
 195  
 196          $out .= \html_writer::end_div(); // End row3.
 197  
 198          if ($filter->target == 'pdf') {
 199              // Converting divs to spans for better PDF display.
 200              $out = str_replace(['<div>', '</div>'], ['<span>', '</span>'], $out);
 201          }
 202  
 203          return $out;
 204      }
 205  
 206      /**
 207       * Return the path to use for PDF images.
 208       *
 209       * @return string
 210       */
 211      private function image_path(): string {
 212          global $CFG;
 213          return $CFG->wwwroot . '/admin/tool/brickfield/pix/pdf/';
 214      }
 215  
 216      /**
 217       * Renders the accessability report using the pdflib.
 218       *
 219       * @param \stdClass $data Report data.
 220       * @param filter $filter Display filters.
 221       * @throws \coding_exception
 222       * @throws \dml_exception
 223       * @throws \moodle_exception
 224       * @return void
 225       */
 226      public function pdf_renderer(\stdClass $data, filter $filter) {
 227          $pdf = new bfpdf();
 228  
 229          $pdf->setFooterFont(Array('Helvetica', '', 10));
 230          $pdf->setPrintHeader(false);
 231          $pdf->SetCreator(PDF_CREATOR);
 232          $pdf->SetAuthor('Brickfield Accessibility Report');
 233          $pdf->SetTitle('Brickfield Accessibility Report');
 234          $pdf->SetFont('Helvetica');
 235  
 236          // Set default monospaced font.
 237          $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
 238  
 239          // Set margins.
 240          $pdf->SetMargins(20, 20, 20, 100);
 241  
 242          $pdf->AddPage('P');
 243          $errorreporting = error_reporting(0);
 244  
 245          // Get current date for current user.
 246          $date = new \DateTime("now", \core_date::get_user_timezone_object());
 247          $pdf->SetLineWidth(0.0);
 248  
 249          $html = '
 250              <h1>' . get_string('accessibilityreport', manager::PLUGINNAME) . '</h1>
 251              <h2>'. accessibility::get_title($filter, $data->countdata) .'</h2>
 252              <p>' . userdate($date->getTimestamp()) . '</p>
 253              <table cellspacing="0" cellpadding="1">
 254                  <tr>
 255                      <td>
 256                          <img src="' . $this->image_path() . 'tachometer-alt-solid.svg" width="15" height="15">' .
 257                          ' <td style="line-height: 10px;"> ' .
 258                          get_string('totalactivitiescount', manager::PLUGINNAME, $data->combodata['total']) .
 259                          '</td></td>
 260                      <td>
 261                          <img src="' . $this->image_path() . 'check-square-regular.svg" width="15" height="15">' .
 262                          ' <td style="line-height: 10px;"> ' .
 263                          get_string('passedcount', manager::PLUGINNAME, $data->combodata['passed']) .
 264                          '</td></td>
 265                      <td>
 266                          <img src="' . $this->image_path() . 'times-circle-regular.svg" width="15" height="15">' .
 267                          ' <td style="line-height: 10px;"> ' .
 268                          get_string('failedcount', manager::PLUGINNAME, $data->combodata['failed']) .
 269                          '</td></td>
 270                  </tr>
 271              </table>';
 272  
 273          $pdf->writeHTML($html);
 274  
 275          if (!empty($data->checkcountdata)) {
 276              $pdf->writeHTML($this->get_errors_table($data));
 277  
 278              $tablegroup = '<table><tr>
 279                  <td width="45%">'. $this->get_group_table($data) . '</td>
 280                  <td width="10%"></td>
 281                  <td width="45%">'. $this->get_inaccessible_table($data) . '</td>
 282              </tr></table>';
 283  
 284              $pdf->writeHTML($tablegroup);
 285  
 286              $pdf->AddPage('P');
 287          } else {
 288              $pdf->writeHTML('<div>'.get_string('noerrorsfound', manager::PLUGINNAME).'</div><div></div>');
 289          }
 290  
 291          $pdf->writeHTML($this->get_ratio_table($data));
 292  
 293          // Output the pdf.
 294          @$pdf->Output(get_string('pdf:filename', 'tool_brickfield', $filter->courseid).
 295              '_' . userdate($date->getTimestamp(), '%Y_%m_%d') . '.pdf', 'D');
 296          error_reporting($errorreporting);
 297  
 298      }
 299  
 300      /**
 301       * Builds the HTML for a styled table used in the pdf report.
 302       *
 303       * @param array $headers The headers of the table.
 304       * @param array $data The table data.
 305       * @param string $title The title of the table.
 306       * @param array $widths The widths of the table columns.
 307       * @return string The HTML code of the table.
 308       */
 309      public function render_table(array $headers, array $data, string $title, array $widths): string {
 310          $numheaders = count($headers);
 311          $html = '';
 312          $html .= '<table cellspacing="0" cellpadding="5"  style="background-color: #ffffff;">';
 313          $html .= '<tr><td style="border-bottom:.1 solid #333333;" colspan="'.$numheaders.'"><h3>'.$title.'</h3></td></tr>';
 314          $html .= '<tr>';
 315  
 316          for ($i = 0; $i < $numheaders; ++$i) {
 317              $align = $i > 0 ? "center" : "left";
 318              $html .= '<th style="text-align:'.$align.';" width="'.$widths[$i].'">';
 319              $html .= '<b>'.$headers[$i].'</b>';
 320              $html .= '</th>';
 321          }
 322          $html .= '</tr>';
 323          $j = 1;
 324          foreach ($data as $row) {
 325              ++$j;
 326              $color = $j % 2 == 1 ? "#FFFFFF" : "#F2F2F2";
 327              $html .= '<tr border ="1" style="background-color:'.$color.';">';
 328              for ($i = 0; $i < $numheaders; ++$i) {
 329                  $align = $i > 0 ? "center" : "left";
 330                  $html .= '<td width="'.$widths[$i].'" style="text-align:'.$align.';, background-color:'.$color.';">';
 331                  $html .= $row[$i];
 332                  $html .= '</td>';
 333              }
 334              $html .= '</tr>';
 335          }
 336          $html .= '</table>';
 337          return $html;
 338      }
 339  
 340      /**
 341       * Gets the Activity Pass Ratio table.
 342       *
 343       * @param \stdClass $data Report data.
 344       * @return string The HTML code of the table.
 345       */
 346      public function get_ratio_table(\stdClass $data): string {
 347          $headers = [
 348          get_string('tbltarget', manager::PLUGINNAME),
 349          get_string('passed', manager::PLUGINNAME),
 350          get_string('failed', manager::PLUGINNAME),
 351          get_string('total')
 352          ];
 353  
 354          $tabledata = [];
 355          foreach ($data->combotardata as $tar => $tarvalue) {
 356              $tabledata[] = [
 357                  $data->tarlabels[$tar],
 358                  $tarvalue['total'] - $tarvalue['failed'],
 359                  $tarvalue['failed'],
 360                  $tarvalue['total']
 361              ];
 362          }
 363  
 364          return $this->render_table(
 365          $headers,
 366          $tabledata,
 367          get_string('targetratio', manager::PLUGINNAME),
 368          ["40%", "20%", "20%", "20%"]
 369          );
 370      }
 371  
 372      /**
 373       * Gets the Check Errors table.
 374       *
 375       * @param \stdClass $data Report data.
 376       * @return string The HTML code of the table.
 377       */
 378      public function get_group_table(\stdClass $data): string {
 379          $headers = [
 380          get_string('checktype', manager::PLUGINNAME),
 381          get_string('count', manager::PLUGINNAME),
 382          ];
 383  
 384          $tabledata = [];
 385  
 386          // Numbers are constants from \tool_brickfield\area_base::checkgroup.
 387          $icons = [
 388              area_base::CHECKGROUP_IMAGE  => $this->image_path() . 'image-regular.svg',
 389              area_base::CHECKGROUP_LAYOUT => $this->image_path() . 'th-large-solid.svg',
 390              area_base::CHECKGROUP_LINK   => $this->image_path() . 'link.png',
 391              area_base::CHECKGROUP_MEDIA  => $this->image_path() . 'play-circle-regular.svg',
 392              area_base::CHECKGROUP_TABLE  => $this->image_path() . 'table-solid.svg',
 393              area_base::CHECKGROUP_TEXT   => $this->image_path() . 'font-solid.svg',
 394          ];
 395  
 396          foreach ($data->groupdata as $key => $group) {
 397              $checkgroup = area_base::checkgroup_name($key);
 398              $icon = $icons[$key];
 399              $tabledata[] = ['<img src="'.$icon.'" width="15" height="15">' . ' ' .' <td style="line-height: 10px;">  '.
 400                  get_string('checktype:' . $checkgroup, manager::PLUGINNAME).'</td>', $group->errorinstances];
 401          }
 402  
 403          return $this->render_table(
 404              $headers,
 405              $tabledata,
 406              get_string('taberrors', manager::PLUGINNAME),
 407              ["70%", "30%"]
 408          );
 409      }
 410  
 411      /**
 412       * Gets the Failed Activities table.
 413       *
 414       * @param \stdClass $data Report data.
 415       * @return string The HTML code of the table.
 416       */
 417      public function get_inaccessible_table(\stdClass $data): string {
 418          $headers = [get_string('tbltarget', manager::PLUGINNAME),
 419          get_string('count', manager::PLUGINNAME)];
 420  
 421          $tabledata = [];
 422  
 423          foreach ($data->toptargetdata as $key => $value) {
 424              if ($value->errorsum > 0) {
 425                  $tabledata[] = [$value->component, $value->errorsum];
 426              }
 427          }
 428          return $this->render_table(
 429              $headers,
 430              $tabledata,
 431              get_string('toptargets', manager::PLUGINNAME),
 432              ["70%", "30%"]
 433          );
 434      }
 435  
 436      /**
 437       * Gets the Top Errors table.
 438       *
 439       * @param \stdClass $data Report data.
 440       * @return string The HTML code of the table.
 441       */
 442      public function get_errors_table(\stdClass $data): string {
 443          $headers = [get_string('tblcheck', manager::PLUGINNAME), get_string('count', manager::PLUGINNAME)];
 444          $tabledata = [];
 445  
 446          $data->checkcountdata = !empty($data->checkcountdata) ? $data->checkcountdata : [];
 447  
 448          foreach ($data->checkcountdata as $value) {
 449              if ($value->checkcount > 0) {
 450                  $tabledata[] = [$value->checkname, $value->checkcount];
 451              }
 452          }
 453          return $this->render_table($headers, $tabledata, get_string('toperrors', manager::PLUGINNAME), ["80%", "20%"]);
 454      }
 455  }