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 311 and 401] [Versions 400 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  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, ENT_COMPAT);
 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                          get_string('totalactivitiescount', manager::PLUGINNAME, $data->combodata['total']) .
 258                      '</td>
 259                      <td>
 260                          <img src="' . $this->image_path() . 'check-square-regular.svg" width="15" height="15">' .
 261                          get_string('passedcount', manager::PLUGINNAME, $data->combodata['passed']) .
 262                      '</td>
 263                      <td>
 264                          <img src="' . $this->image_path() . 'times-circle-regular.svg" width="15" height="15">' .
 265                          get_string('failedcount', manager::PLUGINNAME, $data->combodata['failed']) .
 266                      '</td>
 267                  </tr>
 268              </table>';
 269  
 270          $pdf->writeHTML($html);
 271  
 272          if (!empty($data->checkcountdata)) {
 273              $pdf->writeHTML($this->get_errors_table($data));
 274  
 275              $tablegroup = '<table><tr>
 276                  <td width="45%">'. $this->get_group_table($data) . '</td>
 277                  <td width="10%"></td>
 278                  <td width="45%">'. $this->get_inaccessible_table($data) . '</td>
 279              </tr></table>';
 280  
 281              $pdf->writeHTML($tablegroup);
 282  
 283              $pdf->AddPage('P');
 284          } else {
 285              $pdf->writeHTML('<div>'.get_string('noerrorsfound', manager::PLUGINNAME).'</div><div></div>');
 286          }
 287  
 288          $pdf->writeHTML($this->get_ratio_table($data));
 289  
 290          // Output the pdf.
 291          @$pdf->Output(get_string('pdf:filename', 'tool_brickfield', $filter->courseid).
 292              '_' . userdate($date->getTimestamp(), '%Y_%m_%d') . '.pdf', 'D');
 293          error_reporting($errorreporting);
 294  
 295      }
 296  
 297      /**
 298       * Builds the HTML for a styled table used in the pdf report.
 299       *
 300       * @param array $headers The headers of the table.
 301       * @param array $data The table data.
 302       * @param string $title The title of the table.
 303       * @param array $widths The widths of the table columns.
 304       * @return string The HTML code of the table.
 305       */
 306      public function render_table(array $headers, array $data, string $title, array $widths): string {
 307          $numheaders = count($headers);
 308          $html = '';
 309          $html .= '<table cellspacing="0" cellpadding="5"  style="background-color: #ffffff;">';
 310          $html .= '<tr><td style="border-bottom:.1 solid #333333;" colspan="'.$numheaders.'"><h3>'.$title.'</h3></td></tr>';
 311          $html .= '<tr>';
 312  
 313          for ($i = 0; $i < $numheaders; ++$i) {
 314              $align = $i > 0 ? "center" : "left";
 315              $html .= '<th style="text-align:'.$align.';" width="'.$widths[$i].'">';
 316              $html .= '<b>'.$headers[$i].'</b>';
 317              $html .= '</th>';
 318          }
 319          $html .= '</tr>';
 320          $j = 1;
 321          foreach ($data as $row) {
 322              ++$j;
 323              $color = $j % 2 == 1 ? "#FFFFFF" : "#F2F2F2";
 324              $html .= '<tr border ="1" style="background-color:'.$color.';">';
 325              for ($i = 0; $i < $numheaders; ++$i) {
 326                  $align = $i > 0 ? "center" : "left";
 327                  $html .= '<td width="'.$widths[$i].'" style="text-align:'.$align.';, background-color:'.$color.';">';
 328                  $html .= $row[$i];
 329                  $html .= '</td>';
 330              }
 331              $html .= '</tr>';
 332          }
 333          $html .= '</table>';
 334          return $html;
 335      }
 336  
 337      /**
 338       * Gets the Activity Pass Ratio table.
 339       *
 340       * @param \stdClass $data Report data.
 341       * @return string The HTML code of the table.
 342       */
 343      public function get_ratio_table(\stdClass $data): string {
 344          $headers = [
 345          get_string('tbltarget', manager::PLUGINNAME),
 346          get_string('passed', manager::PLUGINNAME),
 347          get_string('failed', manager::PLUGINNAME),
 348          get_string('total')
 349          ];
 350  
 351          $tabledata = [];
 352          foreach ($data->combotardata as $tar => $tarvalue) {
 353              $tabledata[] = [
 354                  $data->tarlabels[$tar],
 355                  $tarvalue['total'] - $tarvalue['failed'],
 356                  $tarvalue['failed'],
 357                  $tarvalue['total']
 358              ];
 359          }
 360  
 361          return $this->render_table(
 362          $headers,
 363          $tabledata,
 364          get_string('targetratio', manager::PLUGINNAME),
 365          ["40%", "20%", "20%", "20%"]
 366          );
 367      }
 368  
 369      /**
 370       * Gets the Check Errors table.
 371       *
 372       * @param \stdClass $data Report data.
 373       * @return string The HTML code of the table.
 374       */
 375      public function get_group_table(\stdClass $data): string {
 376          $headers = [
 377          get_string('checktype', manager::PLUGINNAME),
 378          get_string('count', manager::PLUGINNAME),
 379          ];
 380  
 381          $tabledata = [];
 382  
 383          // Numbers are constants from \tool_brickfield\area_base::checkgroup.
 384          $icons = [
 385              area_base::CHECKGROUP_IMAGE  => $this->image_path() . 'image-regular.svg',
 386              area_base::CHECKGROUP_LAYOUT => $this->image_path() . 'th-large-solid.svg',
 387              area_base::CHECKGROUP_LINK   => $this->image_path() . 'link.png',
 388              area_base::CHECKGROUP_MEDIA  => $this->image_path() . 'play-circle-regular.svg',
 389              area_base::CHECKGROUP_TABLE  => $this->image_path() . 'table-solid.svg',
 390              area_base::CHECKGROUP_TEXT   => $this->image_path() . 'font-solid.svg',
 391          ];
 392  
 393          foreach ($data->groupdata as $key => $group) {
 394              $checkgroup = area_base::checkgroup_name($key);
 395              $icon = $icons[$key];
 396              $tabledata[] = ['<img src="'.$icon.'" width="15" height="15">' . ' ' .
 397                  get_string('checktype:' . $checkgroup, manager::PLUGINNAME), $group->errorinstances];
 398          }
 399  
 400          return $this->render_table(
 401              $headers,
 402              $tabledata,
 403              get_string('taberrors', manager::PLUGINNAME),
 404              ["70%", "30%"]
 405          );
 406      }
 407  
 408      /**
 409       * Gets the Failed Activities table.
 410       *
 411       * @param \stdClass $data Report data.
 412       * @return string The HTML code of the table.
 413       */
 414      public function get_inaccessible_table(\stdClass $data): string {
 415          $headers = [get_string('tbltarget', manager::PLUGINNAME),
 416          get_string('count', manager::PLUGINNAME)];
 417  
 418          $tabledata = [];
 419  
 420          foreach ($data->toptargetdata as $key => $value) {
 421              if ($value->errorsum > 0) {
 422                  $tabledata[] = [$value->component, $value->errorsum];
 423              }
 424          }
 425          return $this->render_table(
 426              $headers,
 427              $tabledata,
 428              get_string('toptargets', manager::PLUGINNAME),
 429              ["70%", "30%"]
 430          );
 431      }
 432  
 433      /**
 434       * Gets the Top Errors table.
 435       *
 436       * @param \stdClass $data Report data.
 437       * @return string The HTML code of the table.
 438       */
 439      public function get_errors_table(\stdClass $data): string {
 440          $headers = [get_string('tblcheck', manager::PLUGINNAME), get_string('count', manager::PLUGINNAME)];
 441          $tabledata = [];
 442  
 443          $data->checkcountdata = !empty($data->checkcountdata) ? $data->checkcountdata : [];
 444  
 445          foreach ($data->checkcountdata as $value) {
 446              if ($value->checkcount > 0) {
 447                  $tabledata[] = [$value->checkname, $value->checkcount];
 448              }
 449          }
 450          return $this->render_table($headers, $tabledata, get_string('toperrors', manager::PLUGINNAME), ["80%", "20%"]);
 451      }
 452  }