/lib/form/datetimeselector.php

https://bitbucket.org/moodle/moodle · PHP · 337 lines · 190 code · 23 blank · 124 comment · 37 complexity · a6625957eb1fb1e87f5c8fbd41b2b9c1 MD5 · raw file

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