Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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  /**
  18   * This file contains the parent class for moodle blocks, block_base.
  19   *
  20   * @package    core_block
  21   * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
  22   */
  23  
  24  /// Constants
  25  
  26  /**
  27   * Block type of list. Contents of block should be set as an associative array in the content object as items ($this->content->items). Optionally include footer text in $this->content->footer.
  28   */
  29  define('BLOCK_TYPE_LIST',    1);
  30  
  31  /**
  32   * Block type of text. Contents of block should be set to standard html text in the content object as items ($this->content->text). Optionally include footer text in $this->content->footer.
  33   */
  34  define('BLOCK_TYPE_TEXT',    2);
  35  /**
  36   * Block type of tree. $this->content->items is a list of tree_item objects and $this->content->footer is a string.
  37   */
  38  define('BLOCK_TYPE_TREE',    3);
  39  
  40  /**
  41   * Class for describing a moodle block, all Moodle blocks derive from this class
  42   *
  43   * @author Jon Papaioannou
  44   * @package core_block
  45   */
  46  class block_base {
  47  
  48      /**
  49       * Internal var for storing/caching translated strings
  50       * @var string $str
  51       */
  52      var $str;
  53  
  54      /**
  55       * The title of the block to be displayed in the block title area.
  56       * @var string $title
  57       */
  58      var $title         = NULL;
  59  
  60      /**
  61       * The name of the block to be displayed in the block title area if the title is empty.
  62       * @var string arialabel
  63       */
  64      var $arialabel         = NULL;
  65  
  66      /**
  67       * The type of content that this block creates. Currently support options - BLOCK_TYPE_LIST, BLOCK_TYPE_TEXT
  68       * @var int $content_type
  69       */
  70      var $content_type  = BLOCK_TYPE_TEXT;
  71  
  72      /**
  73       * An object to contain the information to be displayed in the block.
  74       * @var stdObject $content
  75       */
  76      var $content       = NULL;
  77  
  78      /**
  79       * The initialized instance of this block object.
  80       * @var block $instance
  81       */
  82      var $instance      = NULL;
  83  
  84      /**
  85       * The page that this block is appearing on.
  86       * @var moodle_page
  87       */
  88      public $page       = NULL;
  89  
  90      /**
  91       * This blocks's context.
  92       * @var stdClass
  93       */
  94      public $context    = NULL;
  95  
  96      /**
  97       * An object containing the instance configuration information for the current instance of this block.
  98       * @var stdObject $config
  99       */
 100      var $config        = NULL;
 101  
 102      /**
 103       * How often the cronjob should run, 0 if not at all.
 104       * @var int $cron
 105       */
 106  
 107      var $cron          = NULL;
 108  
 109  /// Class Functions
 110  
 111      /**
 112       * Fake constructor to keep PHP5 happy
 113       *
 114       */
 115      function __construct() {
 116          $this->init();
 117      }
 118  
 119      /**
 120       * Function that can be overridden to do extra cleanup before
 121       * the database tables are deleted. (Called once per block, not per instance!)
 122       */
 123      function before_delete() {
 124      }
 125  
 126      /**
 127       * Returns the block name, as present in the class name,
 128       * the database, the block directory, etc etc.
 129       *
 130       * @return string
 131       */
 132      function name() {
 133          // Returns the block name, as present in the class name,
 134          // the database, the block directory, etc etc.
 135          static $myname;
 136          if ($myname === NULL) {
 137              $myname = strtolower(get_class($this));
 138              $myname = substr($myname, strpos($myname, '_') + 1);
 139          }
 140          return $myname;
 141      }
 142  
 143      /**
 144       * Parent class version of this function simply returns NULL
 145       * This should be implemented by the derived class to return
 146       * the content object.
 147       *
 148       * @return stdObject
 149       */
 150      function get_content() {
 151          // This should be implemented by the derived class.
 152          return NULL;
 153      }
 154  
 155      /**
 156       * Returns the class $title var value.
 157       *
 158       * Intentionally doesn't check if a title is set.
 159       * This is already done in {@link _self_test()}
 160       *
 161       * @return string $this->title
 162       */
 163      function get_title() {
 164          // Intentionally doesn't check if a title is set. This is already done in _self_test()
 165          return $this->title;
 166      }
 167  
 168      /**
 169       * Returns the class $content_type var value.
 170       *
 171       * Intentionally doesn't check if content_type is set.
 172       * This is already done in {@link _self_test()}
 173       *
 174       * @return string $this->content_type
 175       */
 176      function get_content_type() {
 177          // Intentionally doesn't check if a content_type is set. This is already done in _self_test()
 178          return $this->content_type;
 179      }
 180  
 181      /**
 182       * Returns true or false, depending on whether this block has any content to display
 183       * and whether the user has permission to view the block
 184       *
 185       * @return boolean
 186       */
 187      function is_empty() {
 188          if ( !has_capability('moodle/block:view', $this->context) ) {
 189              return true;
 190          }
 191  
 192          $this->get_content();
 193          return(empty($this->content->text) && empty($this->content->footer));
 194      }
 195  
 196      /**
 197       * First sets the current value of $this->content to NULL
 198       * then calls the block's {@link get_content()} function
 199       * to set its value back.
 200       *
 201       * @return stdObject
 202       */
 203      function refresh_content() {
 204          // Nothing special here, depends on content()
 205          $this->content = NULL;
 206          return $this->get_content();
 207      }
 208  
 209      /**
 210       * Return a block_contents object representing the full contents of this block.
 211       *
 212       * This internally calls ->get_content(), and then adds the editing controls etc.
 213       *
 214       * You probably should not override this method, but instead override
 215       * {@link html_attributes()}, {@link formatted_contents()} or {@link get_content()},
 216       * {@link hide_header()}, {@link (get_edit_controls)}, etc.
 217       *
 218       * @return block_contents a representation of the block, for rendering.
 219       * @since Moodle 2.0.
 220       */
 221      public function get_content_for_output($output) {
 222          global $CFG;
 223  
 224          // We can exit early if the current user doesn't have the capability to view the block.
 225          if (!has_capability('moodle/block:view', $this->context)) {
 226              return null;
 227          }
 228  
 229          $bc = new block_contents($this->html_attributes());
 230          $bc->attributes['data-block'] = $this->name();
 231          $bc->blockinstanceid = $this->instance->id;
 232          $bc->blockpositionid = $this->instance->blockpositionid;
 233  
 234          if ($this->instance->visible) {
 235              $bc->content = $this->formatted_contents($output);
 236              if (!empty($this->content->footer)) {
 237                  $bc->footer = $this->content->footer;
 238              }
 239          } else {
 240              $bc->add_class('invisibleblock');
 241          }
 242  
 243          if (!$this->hide_header()) {
 244              $bc->title = $this->title;
 245          }
 246  
 247          if (empty($bc->title)) {
 248              $bc->arialabel = new lang_string('pluginname', get_class($this));
 249              $this->arialabel = $bc->arialabel;
 250          }
 251  
 252          if ($this->page->user_is_editing() && $this->instance_can_be_edited()) {
 253              $bc->controls = $this->page->blocks->edit_controls($this);
 254          } else {
 255              // we must not use is_empty on hidden blocks
 256              if ($this->is_empty() && !$bc->controls) {
 257                  return null;
 258              }
 259          }
 260  
 261          if (empty($CFG->allowuserblockhiding)
 262                  || (empty($bc->content) && empty($bc->footer))
 263                  || !$this->instance_can_be_collapsed()) {
 264              $bc->collapsible = block_contents::NOT_HIDEABLE;
 265          } else if (get_user_preferences('block' . $bc->blockinstanceid . 'hidden', false)) {
 266              $bc->collapsible = block_contents::HIDDEN;
 267          } else {
 268              $bc->collapsible = block_contents::VISIBLE;
 269          }
 270  
 271          if ($this->instance_can_be_docked() && !$this->hide_header()) {
 272              $bc->dockable = true;
 273          }
 274  
 275          $bc->annotation = ''; // TODO MDL-19398 need to work out what to say here.
 276  
 277          return $bc;
 278      }
 279  
 280  
 281      /**
 282       * Return an object containing all the block content to be returned by external functions.
 283       *
 284       * If your block is returning formatted content or provide files for download, you should override this method to use the
 285       * external_format_text, external_format_string functions for formatting or external_util::get_area_files for files.
 286       *
 287       * @param  core_renderer $output the rendered used for output
 288       * @return stdClass      object containing the block title, central content, footer and linked files (if any).
 289       * @since  Moodle 3.6
 290       */
 291      public function get_content_for_external($output) {
 292          $bc = new stdClass;
 293          $bc->title = null;
 294          $bc->content = null;
 295          $bc->contentformat = FORMAT_HTML;
 296          $bc->footer = null;
 297          $bc->files = [];
 298  
 299          if ($this->instance->visible) {
 300              $bc->content = $this->formatted_contents($output);
 301              if (!empty($this->content->footer)) {
 302                  $bc->footer = $this->content->footer;
 303              }
 304          }
 305  
 306          if (!$this->hide_header()) {
 307              $bc->title = $this->title;
 308          }
 309  
 310          return $bc;
 311      }
 312  
 313      /**
 314       * Return the plugin config settings for external functions.
 315       *
 316       * In some cases the configs will need formatting or be returned only if the current user has some capabilities enabled.
 317       *
 318       * @return stdClass the configs for both the block instance and plugin (as object with name -> value)
 319       * @since Moodle 3.8
 320       */
 321      public function get_config_for_external() {
 322          return (object) [
 323              'instance' => new stdClass(),
 324              'plugin' => new stdClass(),
 325          ];
 326      }
 327  
 328      /**
 329       * Convert the contents of the block to HTML.
 330       *
 331       * This is used by block base classes like block_list to convert the structured
 332       * $this->content->list and $this->content->icons arrays to HTML. So, in most
 333       * blocks, you probaby want to override the {@link get_contents()} method,
 334       * which generates that structured representation of the contents.
 335       *
 336       * @param $output The core_renderer to use when generating the output.
 337       * @return string the HTML that should appearn in the body of the block.
 338       * @since Moodle 2.0.
 339       */
 340      protected function formatted_contents($output) {
 341          $this->get_content();
 342          $this->get_required_javascript();
 343          if (!empty($this->content->text)) {
 344              return $this->content->text;
 345          } else {
 346              return '';
 347          }
 348      }
 349  
 350      /**
 351       * Tests if this block has been implemented correctly.
 352       * Also, $errors isn't used right now
 353       *
 354       * @return boolean
 355       */
 356  
 357      function _self_test() {
 358          // Tests if this block has been implemented correctly.
 359          // Also, $errors isn't used right now
 360          $errors = array();
 361  
 362          $correct = true;
 363          if ($this->get_title() === NULL) {
 364              $errors[] = 'title_not_set';
 365              $correct = false;
 366          }
 367          if (!in_array($this->get_content_type(), array(BLOCK_TYPE_LIST, BLOCK_TYPE_TEXT, BLOCK_TYPE_TREE))) {
 368              $errors[] = 'invalid_content_type';
 369              $correct = false;
 370          }
 371          //following selftest was not working when roles&capabilities were used from block
 372  /*        if ($this->get_content() === NULL) {
 373              $errors[] = 'content_not_set';
 374              $correct = false;
 375          }*/
 376          $formats = $this->applicable_formats();
 377          if (empty($formats) || array_sum($formats) === 0) {
 378              $errors[] = 'no_formats';
 379              $correct = false;
 380          }
 381  
 382          return $correct;
 383      }
 384  
 385      /**
 386       * Subclasses should override this and return true if the
 387       * subclass block has a settings.php file.
 388       *
 389       * @return boolean
 390       */
 391      function has_config() {
 392          return false;
 393      }
 394  
 395      /**
 396       * Default behavior: save all variables as $CFG properties
 397       * You don't need to override this if you 're satisfied with the above
 398       *
 399       * @deprecated since Moodle 2.9 MDL-49385 - Please use Admin Settings functionality to save block configuration.
 400       */
 401      function config_save($data) {
 402          throw new coding_exception('config_save() can not be used any more, use Admin Settings functionality to save block configuration.');
 403      }
 404  
 405      /**
 406       * Which page types this block may appear on.
 407       *
 408       * The information returned here is processed by the
 409       * {@link blocks_name_allowed_in_format()} function. Look there if you need
 410       * to know exactly how this works.
 411       *
 412       * Default case: everything except mod and tag.
 413       *
 414       * @return array page-type prefix => true/false.
 415       */
 416      function applicable_formats() {
 417          // Default case: the block can be used in courses and site index, but not in activities
 418          return array('all' => true, 'mod' => false, 'tag' => false);
 419      }
 420  
 421  
 422      /**
 423       * Default return is false - header will be shown
 424       * @return boolean
 425       */
 426      function hide_header() {
 427          return false;
 428      }
 429  
 430      /**
 431       * Return any HTML attributes that you want added to the outer <div> that
 432       * of the block when it is output.
 433       *
 434       * Because of the way certain JS events are wired it is a good idea to ensure
 435       * that the default values here still get set.
 436       * I found the easiest way to do this and still set anything you want is to
 437       * override it within your block in the following way
 438       *
 439       * <code php>
 440       * function html_attributes() {
 441       *    $attributes = parent::html_attributes();
 442       *    $attributes['class'] .= ' mynewclass';
 443       *    return $attributes;
 444       * }
 445       * </code>
 446       *
 447       * @return array attribute name => value.
 448       */
 449      function html_attributes() {
 450          $attributes = array(
 451              'id' => 'inst' . $this->instance->id,
 452              'class' => 'block_' . $this->name() . ' block',
 453              'role' => $this->get_aria_role()
 454          );
 455          if ($this->hide_header()) {
 456              $attributes['class'] .= ' no-header';
 457          }
 458          if ($this->instance_can_be_docked() && get_user_preferences('docked_block_instance_' . $this->instance->id, 0)) {
 459              $attributes['class'] .= ' dock_on_load';
 460          }
 461          return $attributes;
 462      }
 463  
 464      /**
 465       * Set up a particular instance of this class given data from the block_insances
 466       * table and the current page. (See {@link block_manager::load_blocks()}.)
 467       *
 468       * @param stdClass $instance data from block_insances, block_positions, etc.
 469       * @param moodle_page $the page this block is on.
 470       */
 471      function _load_instance($instance, $page) {
 472          if (!empty($instance->configdata)) {
 473              $this->config = unserialize_object(base64_decode($instance->configdata));
 474          }
 475          $this->instance = $instance;
 476          $this->context = context_block::instance($instance->id);
 477          $this->page = $page;
 478          $this->specialization();
 479      }
 480  
 481      /**
 482       * Allows the block to load any JS it requires into the page.
 483       *
 484       * By default this function simply permits the user to dock the block if it is dockable.
 485       *
 486       * Left null as of MDL-64506.
 487       */
 488      function get_required_javascript() {
 489      }
 490  
 491      /**
 492       * This function is called on your subclass right after an instance is loaded
 493       * Use this function to act on instance data just after it's loaded and before anything else is done
 494       * For instance: if your block will have different title's depending on location (site, course, blog, etc)
 495       */
 496      function specialization() {
 497          // Just to make sure that this method exists.
 498      }
 499  
 500      /**
 501       * Is each block of this type going to have instance-specific configuration?
 502       * Normally, this setting is controlled by {@link instance_allow_multiple()}: if multiple
 503       * instances are allowed, then each will surely need its own configuration. However, in some
 504       * cases it may be necessary to provide instance configuration to blocks that do not want to
 505       * allow multiple instances. In that case, make this function return true.
 506       * I stress again that this makes a difference ONLY if {@link instance_allow_multiple()} returns false.
 507       * @return boolean
 508       */
 509      function instance_allow_config() {
 510          return false;
 511      }
 512  
 513      /**
 514       * Are you going to allow multiple instances of each block?
 515       * If yes, then it is assumed that the block WILL USE per-instance configuration
 516       * @return boolean
 517       */
 518      function instance_allow_multiple() {
 519          // Are you going to allow multiple instances of each block?
 520          // If yes, then it is assumed that the block WILL USE per-instance configuration
 521          return false;
 522      }
 523  
 524      /**
 525       * Serialize and store config data
 526       */
 527      function instance_config_save($data, $nolongerused = false) {
 528          global $DB;
 529          $DB->update_record('block_instances', ['id' => $this->instance->id,
 530                  'configdata' => base64_encode(serialize($data)), 'timemodified' => time()]);
 531      }
 532  
 533      /**
 534       * Replace the instance's configuration data with those currently in $this->config;
 535       */
 536      function instance_config_commit($nolongerused = false) {
 537          global $DB;
 538          $this->instance_config_save($this->config);
 539      }
 540  
 541      /**
 542       * Do any additional initialization you may need at the time a new block instance is created
 543       * @return boolean
 544       */
 545      function instance_create() {
 546          return true;
 547      }
 548  
 549      /**
 550       * Copy any block-specific data when copying to a new block instance.
 551       * @param int $fromid the id number of the block instance to copy from
 552       * @return boolean
 553       */
 554      public function instance_copy($fromid) {
 555          return true;
 556      }
 557  
 558      /**
 559       * Delete everything related to this instance if you have been using persistent storage other than the configdata field.
 560       * @return boolean
 561       */
 562      function instance_delete() {
 563          return true;
 564      }
 565  
 566      /**
 567       * Allows the block class to have a say in the user's ability to edit (i.e., configure) blocks of this type.
 568       * The framework has first say in whether this will be allowed (e.g., no editing allowed unless in edit mode)
 569       * but if the framework does allow it, the block can still decide to refuse.
 570       * @return boolean
 571       */
 572      function user_can_edit() {
 573          global $USER;
 574  
 575          if (has_capability('moodle/block:edit', $this->context)) {
 576              return true;
 577          }
 578  
 579          // The blocks in My Moodle are a special case.  We want them to inherit from the user context.
 580          if (!empty($USER->id)
 581              && $this->instance->parentcontextid == $this->page->context->id   // Block belongs to this page
 582              && $this->page->context->contextlevel == CONTEXT_USER             // Page belongs to a user
 583              && $this->page->context->instanceid == $USER->id) {               // Page belongs to this user
 584              return has_capability('moodle/my:manageblocks', $this->page->context);
 585          }
 586  
 587          return false;
 588      }
 589  
 590      /**
 591       * Allows the block class to have a say in the user's ability to create new instances of this block.
 592       * The framework has first say in whether this will be allowed (e.g., no adding allowed unless in edit mode)
 593       * but if the framework does allow it, the block can still decide to refuse.
 594       * This function has access to the complete page object, the creation related to which is being determined.
 595       *
 596       * @param moodle_page $page
 597       * @return boolean
 598       */
 599      function user_can_addto($page) {
 600          global $CFG;
 601          require_once($CFG->dirroot . '/user/lib.php');
 602  
 603          // List of formats this block supports.
 604          $formats = $this->applicable_formats();
 605  
 606          // Check if user is trying to add blocks to their profile page.
 607          $userpagetypes = user_page_type_list($page->pagetype, null, null);
 608          if (array_key_exists($page->pagetype, $userpagetypes)) {
 609              $capability = 'block/' . $this->name() . ':addinstance';
 610              return $this->has_add_block_capability($page, $capability)
 611                  && has_capability('moodle/user:manageownblocks', $page->context);
 612          }
 613  
 614          // The blocks in My Moodle are a special case and use a different capability.
 615          $mypagetypes = my_page_type_list($page->pagetype); // Get list of possible my page types.
 616  
 617          if (array_key_exists($page->pagetype, $mypagetypes)) { // Ensure we are on a page with a my page type.
 618              // If the block cannot be displayed on /my it is ok if the myaddinstance capability is not defined.
 619              // Is 'my' explicitly forbidden?
 620              // If 'all' has not been allowed, has 'my' been explicitly allowed?
 621              if ((isset($formats['my']) && $formats['my'] == false)
 622                  || (empty($formats['all']) && empty($formats['my']))) {
 623  
 624                  // Block cannot be added to /my regardless of capabilities.
 625                  return false;
 626              } else {
 627                  $capability = 'block/' . $this->name() . ':myaddinstance';
 628                  return $this->has_add_block_capability($page, $capability)
 629                         && has_capability('moodle/my:manageblocks', $page->context);
 630              }
 631          }
 632          // Check if this is a block only used on /my.
 633          unset($formats['my']);
 634          if (empty($formats)) {
 635              // Block can only be added to /my - return false.
 636              return false;
 637          }
 638  
 639          $capability = 'block/' . $this->name() . ':addinstance';
 640          if ($this->has_add_block_capability($page, $capability)
 641                  && has_capability('moodle/block:edit', $page->context)) {
 642              return true;
 643          }
 644  
 645          return false;
 646      }
 647  
 648      /**
 649       * Returns true if the user can add a block to a page.
 650       *
 651       * @param moodle_page $page
 652       * @param string $capability the capability to check
 653       * @return boolean true if user can add a block, false otherwise.
 654       */
 655      private function has_add_block_capability($page, $capability) {
 656          // Check if the capability exists.
 657          if (!get_capability_info($capability)) {
 658              // Debug warning that the capability does not exist, but no more than once per page.
 659              static $warned = array();
 660              if (!isset($warned[$this->name()])) {
 661                  debugging('The block ' .$this->name() . ' does not define the standard capability ' .
 662                          $capability , DEBUG_DEVELOPER);
 663                  $warned[$this->name()] = 1;
 664              }
 665              // If the capability does not exist, the block can always be added.
 666              return true;
 667          } else {
 668              return has_capability($capability, $page->context);
 669          }
 670      }
 671  
 672      static function get_extra_capabilities() {
 673          return array('moodle/block:view', 'moodle/block:edit');
 674      }
 675  
 676      /**
 677       * Can be overridden by the block to prevent the block from being dockable.
 678       *
 679       * @return bool
 680       *
 681       * Return false as per MDL-64506
 682       */
 683      public function instance_can_be_docked() {
 684          return false;
 685      }
 686  
 687      /**
 688       * If overridden and set to false by the block it will not be hidable when
 689       * editing is turned on.
 690       *
 691       * @return bool
 692       */
 693      public function instance_can_be_hidden() {
 694          return true;
 695      }
 696  
 697      /**
 698       * If overridden and set to false by the block it will not be collapsible.
 699       *
 700       * @return bool
 701       */
 702      public function instance_can_be_collapsed() {
 703          return true;
 704      }
 705  
 706      /**
 707       * If overridden and set to false by the block it will not be editable.
 708       *
 709       * @return bool
 710       */
 711      public function instance_can_be_edited() {
 712          return true;
 713      }
 714  
 715      /** @callback callback functions for comments api */
 716      public static function comment_template($options) {
 717          $ret = <<<EOD
 718  <div class="comment-userpicture">___picture___</div>
 719  <div class="comment-content">
 720      ___name___ - <span>___time___</span>
 721      <div>___content___</div>
 722  </div>
 723  EOD;
 724          return $ret;
 725      }
 726      public static function comment_permissions($options) {
 727          return array('view'=>true, 'post'=>true);
 728      }
 729      public static function comment_url($options) {
 730          return null;
 731      }
 732      public static function comment_display($comments, $options) {
 733          return $comments;
 734      }
 735      public static function comment_add(&$comments, $options) {
 736          return true;
 737      }
 738  
 739      /**
 740       * Returns the aria role attribute that best describes this block.
 741       *
 742       * Region is the default, but this should be overridden by a block is there is a region child, or even better
 743       * a landmark child.
 744       *
 745       * Options are as follows:
 746       *    - landmark
 747       *      - application
 748       *      - banner
 749       *      - complementary
 750       *      - contentinfo
 751       *      - form
 752       *      - main
 753       *      - navigation
 754       *      - search
 755       *
 756       * @return string
 757       */
 758      public function get_aria_role() {
 759          return 'complementary';
 760      }
 761  
 762      /**
 763       * This method can be overriden to add some extra checks to decide whether the block can be added or not to a page.
 764       * It doesn't need to do the standard capability checks as they will be performed by has_add_block_capability().
 765       * This method is user agnostic. If you want to check if a user can add a block or not, you should use user_can_addto().
 766       *
 767       * @param moodle_page $page The page where this block will be added.
 768       * @return bool Whether the block can be added or not to the given page.
 769       */
 770      public function can_block_be_added(moodle_page $page): bool {
 771          return true;
 772      }
 773  }
 774  
 775  /**
 776   * Specialized class for displaying a block with a list of icons/text labels
 777   *
 778   * The get_content method should set $this->content->items and (optionally)
 779   * $this->content->icons, instead of $this->content->text.
 780   *
 781   * @author Jon Papaioannou
 782   * @package core_block
 783   */
 784  
 785  class block_list extends block_base {
 786      var $content_type  = BLOCK_TYPE_LIST;
 787  
 788      function is_empty() {
 789          if ( !has_capability('moodle/block:view', $this->context) ) {
 790              return true;
 791          }
 792  
 793          $this->get_content();
 794          return (empty($this->content->items) && empty($this->content->footer));
 795      }
 796  
 797      protected function formatted_contents($output) {
 798          $this->get_content();
 799          $this->get_required_javascript();
 800          if (!empty($this->content->items)) {
 801              return $output->list_block_contents($this->content->icons, $this->content->items);
 802          } else {
 803              return '';
 804          }
 805      }
 806  
 807      function html_attributes() {
 808          $attributes = parent::html_attributes();
 809          $attributes['class'] .= ' list_block';
 810          return $attributes;
 811      }
 812  
 813  }
 814  
 815  /**
 816   * Specialized class for displaying a tree menu.
 817   *
 818   * The {@link get_content()} method involves setting the content of
 819   * <code>$this->content->items</code> with an array of {@link tree_item}
 820   * objects (these are the top-level nodes). The {@link tree_item::children}
 821   * property may contain more tree_item objects, and so on. The tree_item class
 822   * itself is abstract and not intended for use, use one of it's subclasses.
 823   *
 824   * Unlike {@link block_list}, the icons are specified as part of the items,
 825   * not in a separate array.
 826   *
 827   * @author Alan Trick
 828   * @package core_block
 829   * @internal this extends block_list so we get is_empty() for free
 830   */
 831  class block_tree extends block_list {
 832  
 833      /**
 834       * @var int specifies the manner in which contents should be added to this
 835       * block type. In this case <code>$this->content->items</code> is used with
 836       * {@link tree_item}s.
 837       */
 838      public $content_type = BLOCK_TYPE_TREE;
 839  
 840      /**
 841       * Make the formatted HTML ouput.
 842       *
 843       * Also adds the required javascript call to the page output.
 844       *
 845       * @param core_renderer $output
 846       * @return string HTML
 847       */
 848      protected function formatted_contents($output) {
 849          // based of code in admin_tree
 850          global $PAGE; // TODO change this when there is a proper way for blocks to get stuff into head.
 851          static $eventattached;
 852          if ($eventattached===null) {
 853              $eventattached = true;
 854          }
 855          if (!$this->content) {
 856              $this->content = new stdClass;
 857              $this->content->items = array();
 858          }
 859          $this->get_required_javascript();
 860          $this->get_content();
 861          $content = $output->tree_block_contents($this->content->items,array('class'=>'block_tree list'));
 862          if (isset($this->id) && !is_numeric($this->id)) {
 863              $content = $output->box($content, 'block_tree_box', $this->id);
 864          }
 865          return $content;
 866      }
 867  }