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.
   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\local\tool;
  18  
  19  use tool_brickfield\manager;
  20  
  21  /**
  22   * Brickfield accessibility tool base class.
  23   *
  24   * All common properties and methods for all tool types.
  25   *
  26   * @package     tool_brickfield
  27   * @copyright  2020 onward: Brickfield Education Labs, www.brickfield.ie
  28   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  29   */
  30  abstract class tool {
  31  
  32      /** @var string[] All of the tools provided. */
  33      const TOOLNAMES = ['errors', 'activityresults', 'checktyperesults', 'printable', 'advanced'];
  34  
  35      /** @var string base64 bitmap image type */
  36      const BASE64_BMP = 'data:image/bmp;base64';
  37  
  38      /** @var string base64 gif image type */
  39      const BASE64_GIF = 'data:image/gif;base64';
  40  
  41      /** @var string base64 jpg image type */
  42      const BASE64_JPG = 'data:image/jpeg;base64';
  43  
  44      /** @var string base64 png image type */
  45      const BASE64_PNG = 'data:image/png;base64';
  46  
  47      /** @var string base64 svg image type */
  48      const BASE64_SVG = 'data:image/svg+xml;base64';
  49  
  50      /** @var string base64 webp image type */
  51      const BASE64_WEBP = 'data:image/webp;base64';
  52  
  53      /** @var string base64 ico image type */
  54      const BASE64_ICO = 'data:image/x-icon;base64';
  55  
  56      /** @var null Generic object for data used in tool renderers/templates. */
  57      private $data = null;
  58  
  59      /** @var  filter Any filter being used for tool display. */
  60      private $filter;
  61  
  62      /** @var string Error message if there is one. */
  63      private $errormessage = '';
  64  
  65      /**
  66       * Get a mapping of tool shortname => class name.
  67       *
  68       * @return  string[]
  69       */
  70      protected static function get_tool_classnames(): array {
  71          $tools = [];
  72  
  73          foreach (self::TOOLNAMES as $toolname) {
  74              $tools[$toolname] = "tool_brickfield\\local\\tool\\{$toolname}";
  75          }
  76  
  77          return $tools;
  78      }
  79  
  80      /**
  81       * Return an array with one of each tool instance.
  82       *
  83       * @return tool[]
  84       */
  85      public static function build_all_accessibilitytools(): array {
  86          return array_map(function($classname) {
  87              return new $classname();
  88          }, self::get_tool_classnames());
  89      }
  90  
  91      /**
  92       * Get a list of formal tool names for each tool.
  93       *
  94       * @return  string[]
  95       */
  96      public static function get_tool_names(): array {
  97          return array_map(function($classname) {
  98              return $classname::toolname();
  99          }, self::get_tool_classnames());
 100      }
 101  
 102      /**
 103       * Provide a lowercase name identifying this plugin. Should really be the same as the directory name.
 104       * @return string
 105       */
 106      abstract public function pluginname();
 107  
 108      /**
 109       * Provide a name for this tool, suitable for display on pages.
 110       * @return mixed
 111       */
 112      abstract public static function toolname();
 113  
 114      /**
 115       * Provide a short name for this tool, suitable for menus and selectors.
 116       * @return mixed
 117       */
 118      abstract public static function toolshortname();
 119  
 120      /**
 121       * Fetch the data for renderer / template display. Classes must implement this.
 122       * @return \stdClass
 123       */
 124      abstract protected function fetch_data(): \stdClass;
 125  
 126      /**
 127       * Return the data needed for the renderer.
 128       * @return \stdClass
 129       * @throws \coding_exception
 130       */
 131      public function get_data(): \stdClass {
 132          if (!$this->filter) {
 133              throw new \coding_exception('Filter has not been set.');
 134          }
 135  
 136          if (empty($this->data)) {
 137              $this->data = $this->fetch_data();
 138          }
 139  
 140          return $this->data;
 141      }
 142  
 143      /**
 144       * Implementing class should set the 'valid' property when get_data is called.
 145       * @return bool
 146       */
 147      public function data_is_valid(): bool {
 148          $data = $this->get_data();
 149          return (!empty($data->valid));
 150      }
 151  
 152      /**
 153       * Implementing class should set an error string if data is invalidated in 'get_data';
 154       * @return string
 155       */
 156      public function data_error(): string {
 157          if (!$this->data_is_valid()) {
 158              return $this->data->error;
 159          } else {
 160              return '';
 161          }
 162      }
 163  
 164      /**
 165       * Setter for filter property.
 166       * @param filter $filter
 167       * @throws \coding_exception
 168       */
 169      public function set_filter(filter $filter): void {
 170          if ($this->filter) {
 171              throw new \coding_exception('Filter can only be set once.');
 172          }
 173  
 174          $this->filter = $filter;
 175      }
 176  
 177      /**
 178       * Getter for filter property.
 179       * @return filter|null
 180       */
 181      public function get_filter(): ?filter {
 182          return $this->filter;
 183      }
 184  
 185      /**
 186       * Returns the output target for this tool's filter.
 187       *
 188       * @return string
 189       * @throws \coding_exception
 190       */
 191      public function get_output_target() {
 192          $filter = $this->get_filter();
 193          if (!$filter) {
 194              throw new \coding_exception('Filter has not been set.');
 195          }
 196          return $filter->target;
 197      }
 198  
 199      /**
 200       * Get the HTML output for display.
 201       *
 202       * @return mixed
 203       */
 204      public function get_output() {
 205          global $PAGE;
 206  
 207          $data = $this->get_data();
 208          $filter = $this->get_filter();
 209          $renderer = $PAGE->get_renderer('tool_brickfield', $this->pluginname());
 210          return $renderer->display($data, $filter);
 211      }
 212  
 213      /**
 214       * Return the defined toolname.
 215       *
 216       * @return mixed
 217       */
 218      public function get_toolname(): string {
 219          return static::toolname();
 220      }
 221  
 222      /**
 223       * Return the defined toolshortname.
 224       *
 225       * @return mixed
 226       */
 227      public function get_toolshortname(): string {
 228          return static::toolshortname();
 229      }
 230  
 231      /**
 232       * Verify that accessibility tools can be accessed in the provided context.
 233       * @param filter $filter
 234       * @param \context $context
 235       * @return bool
 236       * @throws \coding_exception
 237       * @throws \dml_exception
 238       */
 239      public function can_access(filter $filter, \context $context = null): bool {
 240          return $filter->can_access($context);
 241      }
 242  
 243      /**
 244       * Return any defined error message.
 245       *
 246       * @return string
 247       */
 248      public function get_error_message(): string {
 249          return $this->errormessage;
 250      }
 251  
 252      /**
 253       * Get module label for display
 254       * @param string $modulename
 255       * @return string
 256       * @throws \coding_exception
 257       */
 258      public static function get_module_label(string $modulename): string {
 259          if (get_string_manager()->string_exists('pluginname', $modulename)) {
 260              $modulename = get_string('pluginname', $modulename);
 261          } else {
 262              $modulename = get_string($modulename, manager::PLUGINNAME);
 263          }
 264          return($modulename);
 265      }
 266  
 267      /**
 268       * Get instance name for display
 269       * @param string $component
 270       * @param string $table
 271       * @param int $cmid
 272       * @param int $courseid
 273       * @param int $categoryid
 274       * @return string
 275       */
 276      public static function get_instance_name(string $component, string $table, ?int $cmid, ?int $courseid,
 277          ?int $categoryid): string {
 278          global $DB;
 279  
 280          $instancename = '';
 281          if (empty($component)) {
 282              return $instancename;
 283          }
 284          if ($component == 'core_course') {
 285              if (($table == 'course_categories') && ($categoryid != 0) && ($categoryid != null)) {
 286                  $instancename = $DB->get_field($table, 'name', ['id' => $categoryid]);
 287                  return get_string('category') . ' - ' . $instancename;
 288              }
 289              if (($courseid == 0) || ($courseid == null)) {
 290                  return $instancename;
 291              }
 292              $thiscourse = get_fast_modinfo($courseid)->get_course();
 293              $instancename = $thiscourse->shortname;
 294          } else if ($component == 'core_question') {
 295              $instancename = get_string('questions', 'question');
 296          } else {
 297              if (($cmid == 0) || ($cmid == null)) {
 298                  return $instancename;
 299              }
 300              $cm = get_fast_modinfo($courseid)->cms[$cmid];
 301              $instancename = $cm->name;
 302          }
 303          $instancename = static::get_module_label($component).' - '.$instancename;
 304          return($instancename);
 305      }
 306  
 307      /**
 308       * Provide arguments required for the toplevel page, using any provided filter.
 309       * @param filter|null $filter
 310       * @return array
 311       */
 312      public static function toplevel_arguments(filter $filter = null): array {
 313          if ($filter !== null) {
 314              return ['courseid' => $filter->courseid, 'categoryid' => $filter->categoryid];
 315          } else {
 316              return [];
 317          }
 318      }
 319  
 320      /**
 321       * Override this to return any tool specific perpage limits.
 322       * @param int $perpage
 323       * @return int
 324       */
 325      public function perpage_limits(int $perpage): int {
 326          return $perpage;
 327      }
 328  
 329      /**
 330       * Return array of base64 image formats.
 331       * @return array
 332       */
 333      public static function base64_img_array(): array {
 334          $base64 = [];
 335          $base64[] = self::BASE64_BMP;
 336          $base64[] = self::BASE64_GIF;
 337          $base64[] = self::BASE64_JPG;
 338          $base64[] = self::BASE64_PNG;
 339          $base64[] = self::BASE64_SVG;
 340          $base64[] = self::BASE64_WEBP;
 341          $base64[] = self::BASE64_ICO;
 342          return $base64;
 343      }
 344  
 345      /**
 346       * Detects if htmlcode contains base64 img data, for HTML display, such as errors page.
 347       * @param string $htmlcode
 348       * @return boolean
 349       */
 350      public static function base64_img_detected(string $htmlcode): bool {
 351          $detected = false;
 352  
 353          // Grab defined base64 img array.
 354          $base64 = self::base64_img_array();
 355          foreach ($base64 as $type) {
 356              // Need to detect this within an img tag.
 357              $pos = stripos($htmlcode, '<img src="'.$type);
 358              if ($pos !== false) {
 359                  $detected = true;
 360                  return $detected;
 361              }
 362          }
 363          return $detected;
 364      }
 365  
 366      /**
 367       * Truncate base64-containing htmlcode for HTML display, such as errors page.
 368       * @param string $htmlcode
 369       * @return string
 370       */
 371      public static function truncate_base64(string $htmlcode): string {
 372          $newhtmlcode = '';
 373          // Parse HTML by " characters.
 374          $sections = explode('"', $htmlcode);
 375          $base64 = self::base64_img_array();
 376          foreach ($sections as $section) {
 377              foreach ($base64 as $type) {
 378                  $pos = stripos($section, $type);
 379                  if ($pos !== false) {
 380                      $section = substr($section, 0, $pos + strlen($type)).'...';
 381                  }
 382              }
 383              $newhtmlcode .= $section.'"';
 384          }
 385          return $newhtmlcode;
 386      }
 387  
 388      /**
 389       * Return the correct language string for the provided check.
 390       *
 391       * @param string $check
 392       * @return string
 393       */
 394      public static function get_check_description(string $check): string {
 395          return get_string('checkdesc:' . str_replace('_', '', $check), manager::PLUGINNAME);
 396      }
 397  }