Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.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 core\output\local\dropdown;
  18  
  19  use core\output\named_templatable;
  20  use renderable;
  21  
  22  /**
  23   * Class to render a dropdown dialog element.
  24   *
  25   * A dropdown dialog allows to render any arbitrary HTML into a dropdown elements triggered
  26   * by a button.
  27   *
  28   * @package    core
  29   * @category   output
  30   * @copyright  2023 Ferran Recio <ferran@moodle.com>
  31   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  32   */
  33  class dialog implements named_templatable, renderable {
  34      /** Dropdown dialog positions. */
  35      public const POSITION = [
  36          'start' => 'dropdown-menu-left',
  37          'end' => 'dropdown-menu-right',
  38      ];
  39  
  40      /** Dropdown dialog positions. */
  41      public const WIDTH = [
  42          'default' => '',
  43          'big' => 'dialog-big',
  44          'small' => 'dialog-small',
  45      ];
  46  
  47      /**
  48       * @var string content of dialog.
  49       */
  50      protected $dialogcontent = '';
  51  
  52      /**
  53       * @var bool if the footer should auto enable or not.
  54       */
  55      protected $buttoncontent = true;
  56  
  57      /**
  58       * @var string trigger button CSS classes.
  59       */
  60      protected $buttonclasses = '';
  61  
  62      /**
  63       * @var string component CSS classes.
  64       */
  65      protected $classes = '';
  66  
  67      /**
  68       * @var string the dropdown position.
  69       */
  70      protected $dropdownposition = self::POSITION['start'];
  71  
  72      /**
  73       * @var string dropdown preferred width.
  74       */
  75      protected $dropdownwidth = self::WIDTH['default'];
  76  
  77  
  78      /**
  79       * @var array extra HTML attributes (attribute => value).
  80       */
  81      protected $extras = [];
  82  
  83      /**
  84       * Constructor.
  85       *
  86       * The definition object could contain the following keys:
  87       * - classes: component CSS classes.
  88       * - buttonclasses: the button CSS classes.
  89       * - dialogwidth: the dropdown width.
  90       * - dropdownposition: the dropdown position.
  91       * - extras: extra HTML attributes (attribute => value).
  92       *
  93       * @param string $buttoncontent the button content
  94       * @param string $dialogcontent the footer content
  95       * @param array $definition an optional array of the element definition
  96       */
  97      public function __construct(string $buttoncontent, string $dialogcontent, array $definition = []) {
  98          $this->buttoncontent = $buttoncontent;
  99          $this->dialogcontent = $dialogcontent;
 100          if (isset($definition['classes'])) {
 101              $this->classes = $definition['classes'];
 102          }
 103          if (isset($definition['buttonclasses'])) {
 104              $this->buttonclasses = $definition['buttonclasses'];
 105          }
 106          if (isset($definition['extras'])) {
 107              $this->extras = $definition['extras'];
 108          }
 109          if (isset($definition['dialogwidth'])) {
 110              $this->dropdownwidth = $definition['dialogwidth'];
 111          }
 112          if (isset($definition['dropdownposition'])) {
 113              $this->dropdownposition = $definition['dropdownposition'];
 114          }
 115      }
 116  
 117      /**
 118       * Set the dialog contents.
 119       *
 120       * @param string $dialogcontent
 121       */
 122      public function set_content(string $dialogcontent) {
 123          $this->dialogcontent = $dialogcontent;
 124      }
 125  
 126      /**
 127       * Set the button contents.
 128       *
 129       * @param string $buttoncontent
 130       * @param string|null $buttonclasses the button classes
 131       */
 132      public function set_button(string $buttoncontent, ?string $buttonclasses = null) {
 133          $this->buttoncontent = $buttoncontent;
 134          if ($buttonclasses !== null) {
 135              $this->buttonclasses = $buttonclasses;
 136          }
 137      }
 138  
 139      /**
 140       * Set the dialog width.
 141       *
 142       * @param string $width
 143       */
 144      public function set_dialog_width(string $width) {
 145          $this->dropdownwidth = $width;
 146      }
 147  
 148      /**
 149       * Add extra classes to trigger butotn.
 150       *
 151       * @param string $buttonclasses the extra classes
 152       */
 153      public function set_button_classes(string $buttonclasses) {
 154          $this->buttonclasses = $buttonclasses;
 155      }
 156  
 157      /**
 158       * Add extra classes to the component.
 159       *
 160       * @param string $classes the extra classes
 161       */
 162      public function set_classes(string $classes) {
 163          $this->classes = $classes;
 164      }
 165  
 166      /**
 167       * Add extra extras to the sticky footer element.
 168       *
 169       * @param string $attribute the extra attribute
 170       * @param string $value the value
 171       */
 172      public function add_extra(string $attribute, string $value) {
 173          $this->extras[$attribute] = $value;
 174      }
 175  
 176      /**
 177       * Set the button element id.
 178       *
 179       * @param string $value the value
 180       */
 181      public function add_button_id(string $value) {
 182          $this->extras['buttonid'] = $value;
 183      }
 184  
 185      /**
 186       * Set the dropdown position.
 187       * @param string $position the position
 188       */
 189      public function set_position(string $position) {
 190          $this->dropdownposition = $position;
 191      }
 192  
 193      /**
 194       * Export this data so it can be used as the context for a mustache template (core/inplace_editable).
 195       *
 196       * @param \renderer_base $output typically, the renderer that's calling this function
 197       * @return array data context for a mustache template
 198       */
 199      public function export_for_template(\renderer_base $output): array {
 200          $extras = [];
 201          // Id is required to add JS controls to the dropdown.
 202          $dropdownid = $this->extras['id'] ?? \html_writer::random_id('dropdownDialog_');
 203          if (isset($this->extras['id'])) {
 204              unset($this->extras['id']);
 205          }
 206          foreach ($this->extras as $attribute => $value) {
 207              $extras[] = [
 208                  'attribute' => $attribute,
 209                  'value' => $value,
 210              ];
 211          }
 212          $data = [
 213              // Id is required for the correct HTML labelling.
 214              'dropdownid' => $dropdownid,
 215              'buttonid' => $this->extras['buttonid'] ?? \html_writer::random_id('dropwdownbutton_'),
 216              'buttoncontent' => (string) $this->buttoncontent,
 217              'dialogcontent' => (string) $this->dialogcontent,
 218              'classes' => $this->classes,
 219              'buttonclasses' => $this->buttonclasses,
 220              'dialogclasses' => $this->dropdownwidth,
 221              'extras' => $extras,
 222          ];
 223          // Bootstrap 4 dropdown position still uses left and right literals.
 224          $data["position"] = $this->dropdownposition;
 225          if (right_to_left()) {
 226              $rltposition = [
 227                  self::POSITION['start'] => self::POSITION['end'],
 228                  self::POSITION['end'] => self::POSITION['end'],
 229              ];
 230              $data["position"] = $rltposition[$this->dropdownposition];
 231          }
 232          return $data;
 233      }
 234  
 235      /**
 236       * Get the name of the template to use for this templatable.
 237       *
 238       * @param \renderer_base $renderer The renderer requesting the template name
 239       * @return string the template name
 240       */
 241      public function get_template_name(\renderer_base $renderer): string {
 242          return 'core/local/dropdown/dialog';
 243      }
 244  }