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 310 and 401] [Versions 311 and 401] [Versions 39 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  
  18  /**
  19   * Group of date input element
  20   *
  21   * Contains class for a group of elements used to input a date.
  22   *
  23   * @package   core_form
  24   * @copyright 2007 Jamie Pratt <me@jamiep.org>
  25   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  
  28  global $CFG;
  29  require_once($CFG->libdir . '/form/group.php');
  30  require_once($CFG->libdir . '/formslib.php');
  31  
  32  /**
  33   * Class for a group of elements used to input a date.
  34   *
  35   * Emulates moodle print_date_selector function
  36   *
  37   * @package   core_form
  38   * @category  form
  39   * @copyright 2007 Jamie Pratt <me@jamiep.org>
  40   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class MoodleQuickForm_date_selector extends MoodleQuickForm_group {
  43  
  44      /**
  45       * Control the fieldnames for form elements.
  46       *
  47       * startyear => int start of range of years that can be selected
  48       * stopyear => int last year that can be selected
  49       * timezone => int|float|string (optional) timezone modifier used for edge case only.
  50       *      If not specified, then date is caclulated based on current user timezone.
  51       *      Note: dst will be calculated for string timezones only
  52       *      {@link https://moodledev.io/docs/apis/subsystems/time#timezone}
  53       * optional => if true, show a checkbox beside the date to turn it on (or off)
  54       * @var array
  55       */
  56      protected $_options = array();
  57  
  58      /**
  59       * @var array These complement separators, they are appended to the resultant HTML.
  60       */
  61      protected $_wrap = array('', '');
  62  
  63      /**
  64       * @var null|bool Keeps track of whether the date selector was initialised using createElement
  65       *                or addElement. If true, createElement was used signifying the element has been
  66       *                added to a group - see MDL-39187.
  67       */
  68      protected $_usedcreateelement = true;
  69  
  70      /**
  71       * constructor
  72       *
  73       * @param string $elementName Element's name
  74       * @param mixed $elementLabel Label(s) for an element
  75       * @param array $options Options to control the element's display
  76       * @param mixed $attributes Either a typical HTML attribute string or an associative array
  77       */
  78      public function __construct($elementName = null, $elementLabel = null, $options = array(), $attributes = null) {
  79          // Get the calendar type used - see MDL-18375.
  80          $calendartype = \core_calendar\type_factory::get_calendar_instance();
  81          $this->_options = array('startyear' => $calendartype->get_min_year(), 'stopyear' => $calendartype->get_max_year(),
  82              'defaulttime' => 0, 'timezone' => 99, 'step' => 1, 'optional' => false);
  83          // TODO MDL-52313 Replace with the call to parent::__construct().
  84          HTML_QuickForm_element::__construct($elementName, $elementLabel, $attributes);
  85          $this->_persistantFreeze = true;
  86          $this->_appendName = true;
  87          $this->_type = 'date_selector';
  88          // set the options, do not bother setting bogus ones
  89          if (is_array($options)) {
  90              foreach ($options as $name => $value) {
  91                  if (isset($this->_options[$name])) {
  92                      if (is_array($value) && is_array($this->_options[$name])) {
  93                          $this->_options[$name] = @array_merge($this->_options[$name], $value);
  94                      } else {
  95                          $this->_options[$name] = $value;
  96                      }
  97                  }
  98              }
  99          }
 100      }
 101  
 102      /**
 103       * Old syntax of class constructor. Deprecated in PHP7.
 104       *
 105       * @deprecated since Moodle 3.1
 106       */
 107      public function MoodleQuickForm_date_selector($elementName = null, $elementLabel = null, $options = array(), $attributes = null) {
 108          debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
 109          self::__construct($elementName, $elementLabel, $options, $attributes);
 110      }
 111  
 112      /**
 113       * This will create date group element constisting of day, month and year.
 114       *
 115       * @access private
 116       */
 117      function _createElements() {
 118          global $OUTPUT;
 119  
 120          // Get the calendar type used - see MDL-18375.
 121          $calendartype = \core_calendar\type_factory::get_calendar_instance();
 122  
 123          $this->_elements = array();
 124  
 125          $dateformat = $calendartype->get_date_order($this->_options['startyear'], $this->_options['stopyear']);
 126          // Reverse date element (Day, Month, Year), in RTL mode.
 127          if (right_to_left()) {
 128              $dateformat = array_reverse($dateformat);
 129          }
 130          // If optional we add a checkbox which the user can use to turn if on.
 131          if ($this->_options['optional']) {
 132              $this->_elements[] = $this->createFormElement('checkbox', 'enabled', null,
 133                  get_string('enable'), $this->getAttributesForFormElement(), true);
 134          }
 135          foreach ($dateformat as $key => $value) {
 136              // E_STRICT creating elements without forms is nasty because it internally uses $this
 137              $this->_elements[] = $this->createFormElement('select', $key, get_string($key, 'form'), $value,
 138                  $this->getAttributesForFormElement(), true);
 139          }
 140          // The YUI2 calendar only supports the gregorian calendar type so only display the calendar image if this is being used.
 141          if ($calendartype->get_name() === 'gregorian') {
 142              $image = $OUTPUT->pix_icon('i/calendar', get_string('calendar', 'calendar'), 'moodle');
 143              $this->_elements[] = $this->createFormElement('link', 'calendar',
 144                      null, '#', $image);
 145          }
 146          foreach ($this->_elements as $element){
 147              if (method_exists($element, 'setHiddenLabel')){
 148                  $element->setHiddenLabel(true);
 149              }
 150          }
 151  
 152      }
 153  
 154      /**
 155       * Called by HTML_QuickForm whenever form event is made on this element
 156       *
 157       * @param string $event Name of event
 158       * @param mixed $arg event arguments
 159       * @param object $caller calling object
 160       * @return bool
 161       */
 162      function onQuickFormEvent($event, $arg, &$caller) {
 163          $this->setMoodleForm($caller);
 164          switch ($event) {
 165              case 'updateValue':
 166                  // Constant values override both default and submitted ones
 167                  // default values are overriden by submitted.
 168                  $value = $this->_findValue($caller->_constantValues);
 169                  if (null === $value) {
 170                      // If no boxes were checked, then there is no value in the array
 171                      // yet we don't want to display default value in this case.
 172                      if ($caller->isSubmitted() && !$caller->is_new_repeat($this->getName())) {
 173                          $value = $this->_findValue($caller->_submitValues);
 174                      } else {
 175                          $value = $this->_findValue($caller->_defaultValues);
 176                      }
 177                  }
 178                  $requestvalue=$value;
 179                  if ($value == 0) {
 180                      $value = time();
 181                  }
 182                  if (!is_array($value)) {
 183                      $calendartype = \core_calendar\type_factory::get_calendar_instance();
 184                      $currentdate = $calendartype->timestamp_to_date_array($value, $this->_options['timezone']);
 185                      $value = array(
 186                          'day' => $currentdate['mday'],
 187                          'month' => $currentdate['mon'],
 188                          'year' => $currentdate['year']);
 189                      // If optional, default to off, unless a date was provided.
 190                      if ($this->_options['optional']) {
 191                          $value['enabled'] = $requestvalue != 0;
 192                      }
 193                  } else {
 194                      $value['enabled'] = isset($value['enabled']);
 195                  }
 196                  if (null !== $value) {
 197                      $this->setValue($value);
 198                  }
 199                  break;
 200              case 'createElement':
 201                  // Optional is an optional param, if its set we need to add a disabledIf rule.
 202                  // If its empty or not specified then its not an optional dateselector.
 203                  if (!empty($arg[2]['optional']) && !empty($arg[0])) {
 204                      // When using the function addElement, rather than createElement, we still
 205                      // enter this case, making this check necessary.
 206                      if ($this->_usedcreateelement) {
 207                          $caller->disabledIf($arg[0] . '[day]', $arg[0] . '[enabled]');
 208                          $caller->disabledIf($arg[0] . '[month]', $arg[0] . '[enabled]');
 209                          $caller->disabledIf($arg[0] . '[year]', $arg[0] . '[enabled]');
 210                      } else {
 211                          $caller->disabledIf($arg[0], $arg[0] . '[enabled]');
 212                      }
 213                  }
 214                  return parent::onQuickFormEvent($event, $arg, $caller);
 215                  break;
 216              case 'addElement':
 217                  $this->_usedcreateelement = false;
 218                  return parent::onQuickFormEvent($event, $arg, $caller);
 219                  break;
 220              default:
 221                  return parent::onQuickFormEvent($event, $arg, $caller);
 222          }
 223      }
 224  
 225      /**
 226       * Returns HTML for advchecbox form element.
 227       *
 228       * @return string
 229       */
 230      function toHtml() {
 231          include_once('HTML/QuickForm/Renderer/Default.php');
 232          $renderer = new HTML_QuickForm_Renderer_Default();
 233          $renderer->setElementTemplate('{element}');
 234          parent::accept($renderer);
 235  
 236          $html = $this->_wrap[0];
 237          if ($this->_usedcreateelement) {
 238              $html .= html_writer::tag('span', $renderer->toHtml(), array('class' => 'fdate_selector'));
 239          } else {
 240              $html .= $renderer->toHtml();
 241          }
 242          $html .= $this->_wrap[1];
 243  
 244          return $html;
 245      }
 246  
 247      /**
 248       * Accepts a renderer
 249       *
 250       * @param HTML_QuickForm_Renderer $renderer An HTML_QuickForm_Renderer object
 251       * @param bool $required Whether a group is required
 252       * @param string $error An error message associated with a group
 253       */
 254      function accept(&$renderer, $required = false, $error = null) {
 255          form_init_date_js();
 256          $renderer->renderElement($this, $required, $error);
 257      }
 258  
 259      /**
 260       * Export for template
 261       *
 262       * @param renderer_base $output
 263       * @return array|stdClass
 264       */
 265      public function export_for_template(renderer_base $output) {
 266          form_init_date_js();
 267          return parent::export_for_template($output);
 268      }
 269  
 270      /**
 271       * Output a timestamp. Give it the name of the group.
 272       *
 273       * @param array $submitValues values submitted.
 274       * @param bool $assoc specifies if returned array is associative
 275       * @return array
 276       */
 277      function exportValue(&$submitValues, $assoc = false) {
 278          $valuearray = array();
 279          foreach ($this->_elements as $element){
 280              $thisexport = $element->exportValue($submitValues[$this->getName()], true);
 281              if ($thisexport!=null){
 282                  $valuearray += $thisexport;
 283              }
 284          }
 285          if (count($valuearray) && isset($valuearray['year'])) {
 286              if($this->_options['optional']) {
 287                  // If checkbox is on, the value is zero, so go no further
 288                  if(empty($valuearray['enabled'])) {
 289                      return $this->_prepareValue(0, $assoc);
 290                  }
 291              }
 292              // Get the calendar type used - see MDL-18375.
 293              $calendartype = \core_calendar\type_factory::get_calendar_instance();
 294              $gregoriandate = $calendartype->convert_to_gregorian($valuearray['year'], $valuearray['month'], $valuearray['day']);
 295              $value = make_timestamp($gregoriandate['year'],
 296                                                        $gregoriandate['month'],
 297                                                        $gregoriandate['day'],
 298                                                        0, 0, 0,
 299                                                        $this->_options['timezone'],
 300                                                        true);
 301  
 302              return $this->_prepareValue($value, $assoc);
 303          } else {
 304              return null;
 305          }
 306      }
 307  }